Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit testing feature branch pull request #8411

Merged
merged 41 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
181f520
Initial implementation of unit testing (from pr #2911)
gshank Aug 14, 2023
24abc37
Merge branch 'main' into unit_testing_feature_branch
gshank Aug 23, 2023
7ea7069
Merge branch 'main' into unit_testing_feature_branch
gshank Aug 28, 2023
42e66fd
8295 unit testing artifacts (#8477)
gshank Aug 29, 2023
b3bcbd5
Merge branch 'main' into unit_testing_feature_branch
gshank Aug 30, 2023
1e64f94
Merge branch 'main' into unit_testing_feature_branch
gshank Aug 31, 2023
120b36e
Merge branch 'main' into unit_testing_feature_branch
gshank Sep 7, 2023
2b376d9
Merge branch 'main' into unit_testing_feature_branch
gshank Sep 11, 2023
12342ca
unit test config: tags & meta (#8565)
MichelleArk Sep 12, 2023
c48e34c
Add additional functional test for unit testing selection, artifacts,…
gshank Sep 13, 2023
3dbf095
Merge branch 'main' into unit_testing_feature_branch
gshank Sep 13, 2023
08ef90a
Merge branch 'main' into unit_testing_feature_branch
gshank Sep 22, 2023
ac719e4
Merge branch 'main' into unit_testing_feature_branch
gshank Sep 26, 2023
bb6fd30
Merge branch 'main' into unit_testing_feature_branch
gshank Oct 2, 2023
5cafb96
Merge branch 'main' into unit_testing_feature_branch
gshank Oct 5, 2023
3b6f9bd
Enable inline csv format in unit testing (#8743)
gshank Oct 5, 2023
df4e4ed
Merge branch 'main' into unit_testing_feature_branch
gshank Oct 12, 2023
f77c226
Merge branch 'main' into unit_testing_feature_branch
gshank Nov 1, 2023
aa91ea4
Support unit testing incremental models (#8891)
MichelleArk Nov 2, 2023
02a3dc5
update unit test key: unit -> unit-tests (#8988)
emmyoop Nov 3, 2023
f629baa
convert to use unit test name at top level key (#8966)
emmyoop Nov 3, 2023
2792e0c
Merge branch 'main' into unit_testing_feature_branch
gshank Nov 8, 2023
3b033ac
csv file fixtures (#9044)
emmyoop Nov 9, 2023
ebf48d2
Unit test support for `state:modified` and `--defer` (#9032)
jtcohen6 Nov 14, 2023
436dae6
Merge branch 'main' into unit_testing_feature_branch
gshank Nov 14, 2023
c6be2d2
Allow use of sources as unit testing inputs (#9059)
gshank Nov 15, 2023
35f579e
Use daff for diff formatting in unit testing (#8984)
MichelleArk Nov 15, 2023
3432436
Fix #8652: Use seed file from disk for unit testing if rows not speci…
aranke Nov 16, 2023
964a728
Merge branch 'main' into unit_testing_feature_branch
gshank Nov 16, 2023
a559259
Merge branch 'unit_testing_feature_branch' of github.com:dbt-labs/dbt…
gshank Nov 16, 2023
e001991
Merge branch 'main' into unit_testing_feature_branch
gshank Nov 21, 2023
3f1ed23
Move unit testing to test and build commands (#9108)
gshank Nov 27, 2023
bf6bffa
Merge branch 'main' into unit_testing_feature_branch
gshank Nov 30, 2023
ca82f54
Enable unit testing in non-root packages (#9184)
gshank Nov 30, 2023
a570a2c
convert test to data_test (#9201)
emmyoop Dec 7, 2023
9a79fba
Make fixtures files full-fledged members of manifest and enable parti…
gshank Dec 7, 2023
4e87f46
Merge branch 'main' into unit_testing_feature_branch
gshank Dec 7, 2023
a0177e3
In build command run unit tests before models (#9273)
gshank Dec 20, 2023
56dfb34
Merge branch 'main' into unit_testing_feature_branch
gshank Jan 2, 2024
9160738
Merge branch 'main' into unit_testing_feature_branch
gshank Jan 12, 2024
42b0b85
Merge branch 'main' into unit_testing_feature_branch
gshank Jan 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20230802-145011.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Initial implementation of unit testing
time: 2023-08-02T14:50:11.391992-04:00
custom:
Author: gshank
Issue: "8287"
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20230828-101825.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Unit test manifest artifacts and selection
time: 2023-08-28T10:18:25.958929-04:00
custom:
Author: gshank
Issue: "8295"
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20230906-234741.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Support config with tags & meta for unit tests
time: 2023-09-06T23:47:41.059915-04:00
custom:
Author: michelleark
Issue: "8294"
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20230928-163205.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Enable inline csv fixtures in unit tests
time: 2023-09-28T16:32:05.573776-04:00
custom:
Author: gshank
Issue: "8626"
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20231101-101845.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Support unit testing incremental models
time: 2023-11-01T10:18:45.341781-04:00
custom:
Author: michelleark
Issue: "8422"
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20231106-194752.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Add support of csv file fixtures to unit testing
time: 2023-11-06T19:47:52.501495-06:00
custom:
Author: emmyoop
Issue: "8290"
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20231107-231006.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Unit tests support --defer and state:modified
time: 2023-11-07T23:10:06.376588-05:00
custom:
Author: jtcohen6
Issue: "8517"
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20231111-191150.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Support source inputs in unit tests
time: 2023-11-11T19:11:50.870494-05:00
custom:
Author: gshank
Issue: "8507"
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20231114-101555.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Use daff to render diff displayed in stdout when unit test fails
time: 2023-11-14T10:15:55.689307-05:00
custom:
Author: michelleark
Issue: "8558"
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20231116-144006.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Move unit testing to test command
time: 2023-11-16T14:40:06.121336-05:00
custom:
Author: gshank
Issue: "8979"
6 changes: 6 additions & 0 deletions .changes/unreleased/Fixes-20231113-154535.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Fixes
body: Use seed file from disk for unit testing if rows not specified in YAML config
time: 2023-11-13T15:45:35.008565Z
custom:
Author: aranke
Issue: "8652"
6 changes: 6 additions & 0 deletions .changes/unreleased/Under the Hood-20230912-190506.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Under the Hood
body: Add unit testing functional tests
time: 2023-09-12T19:05:06.023126-04:00
custom:
Author: gshank
Issue: "8512"
18 changes: 14 additions & 4 deletions core/dbt/adapters/base/relation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
from dataclasses import dataclass, field
from typing import Optional, TypeVar, Any, Type, Dict, Iterator, Tuple, Set, Union, FrozenSet

from dbt.contracts.graph.nodes import SourceDefinition, ManifestNode, ResultNode, ParsedNode
from dbt.contracts.graph.nodes import (
SourceDefinition,
ManifestNode,
ResultNode,
ParsedNode,
UnitTestSourceDefinition,
)
from dbt.contracts.relation import (
RelationType,
ComponentName,
Expand Down Expand Up @@ -201,7 +207,9 @@ def quoted(self, identifier):
)

@classmethod
def create_from_source(cls: Type[Self], source: SourceDefinition, **kwargs: Any) -> Self:
def create_from_source(
cls: Type[Self], source: Union[SourceDefinition, UnitTestSourceDefinition], **kwargs: Any
) -> Self:
source_quoting = source.quoting.to_dict(omit_none=True)
source_quoting.pop("column", None)
quote_policy = deep_merge(
Expand Down Expand Up @@ -263,8 +271,10 @@ def create_from(
node: ResultNode,
**kwargs: Any,
) -> Self:
if node.resource_type == NodeType.Source:
if not isinstance(node, SourceDefinition):
if node.resource_type == NodeType.Source or isinstance(node, UnitTestSourceDefinition):
if not (
isinstance(node, SourceDefinition) or isinstance(node, UnitTestSourceDefinition)
):
raise DbtInternalError(
"type mismatch, expected SourceDefinition but got {}".format(type(node))
)
Expand Down
2 changes: 1 addition & 1 deletion core/dbt/adapters/relation_configs/config_change.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class RelationConfigChangeAction(StrEnum):
drop = "drop"


@dataclass(frozen=True, eq=True, unsafe_hash=True)
@dataclass(frozen=True, eq=True, unsafe_hash=True) # type: ignore
class RelationConfigChange(RelationConfigBase, ABC):
action: RelationConfigChangeAction
context: Hashable # this is usually a RelationConfig, e.g. IndexConfig, but shouldn't be limited
Expand Down
18 changes: 18 additions & 0 deletions core/dbt/clients/agate_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import agate
import datetime
import isodate
import io
import json
import dbt.utils
from typing import Iterable, List, Dict, Union, Optional, Any
Expand Down Expand Up @@ -137,6 +138,23 @@ def table_from_data_flat(data, column_names: Iterable[str]) -> agate.Table:
)


def json_rows_from_table(table: agate.Table) -> List[Dict[str, Any]]:
"Convert a table to a list of row dict objects"
output = io.StringIO()
table.to_json(path=output) # type: ignore

return json.loads(output.getvalue())


def list_rows_from_table(table: agate.Table) -> List[Any]:
"Convert a table to a list of lists, where the first element represents the header"
rows = [[col.name for col in table.columns]]
for row in table.rows:
rows.append(list(row.values()))

return rows


def empty_table():
"Returns an empty Agate table. To be used in place of None"

Expand Down
20 changes: 20 additions & 0 deletions core/dbt/clients/jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,26 @@ def __call__(self, *args, **kwargs):
return self.call_macro(*args, **kwargs)


class UnitTestMacroGenerator(MacroGenerator):
# this makes UnitTestMacroGenerator objects callable like functions
def __init__(
self,
macro_generator: MacroGenerator,
call_return_value: Any,
) -> None:
super().__init__(
macro_generator.macro,
macro_generator.context,
macro_generator.node,
macro_generator.stack,
)
self.call_return_value = call_return_value

def __call__(self, *args, **kwargs):
with self.track_call():
return self.call_return_value


class QueryStringGenerator(BaseMacroGenerator):
def __init__(self, template_str: str, context: Dict[str, Any]) -> None:
super().__init__(context)
Expand Down
20 changes: 17 additions & 3 deletions core/dbt/compilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
from dbt.adapters.factory import get_adapter
from dbt.clients import jinja
from dbt.clients.system import make_directory
from dbt.context.providers import generate_runtime_model_context
from dbt.context.providers import (
generate_runtime_model_context,
generate_runtime_unit_test_context,
)
from dbt.contracts.graph.manifest import Manifest, UniqueID
from dbt.contracts.graph.nodes import (
ManifestNode,
Expand All @@ -21,6 +24,8 @@
GraphMemberNode,
InjectedCTE,
SeedNode,
UnitTestNode,
UnitTestDefinition,
)
from dbt.exceptions import (
GraphDependencyNotFoundError,
Expand All @@ -44,6 +49,7 @@ def print_compile_stats(stats):
names = {
NodeType.Model: "model",
NodeType.Test: "test",
NodeType.Unit: "unit test",
NodeType.Snapshot: "snapshot",
NodeType.Analysis: "analysis",
NodeType.Macro: "macro",
Expand Down Expand Up @@ -91,6 +97,7 @@ def _generate_stats(manifest: Manifest):
stats[NodeType.Macro] += len(manifest.macros)
stats[NodeType.Group] += len(manifest.groups)
stats[NodeType.SemanticModel] += len(manifest.semantic_models)
stats[NodeType.Unit] += len(manifest.unit_tests)

# TODO: should we be counting dimensions + entities?

Expand Down Expand Up @@ -191,6 +198,8 @@ def link_graph(self, manifest: Manifest):
self.link_node(exposure, manifest)
for metric in manifest.metrics.values():
self.link_node(metric, manifest)
for unit_test in manifest.unit_tests.values():
self.link_node(unit_test, manifest)
for saved_query in manifest.saved_queries.values():
self.link_node(saved_query, manifest)

Expand Down Expand Up @@ -291,8 +300,10 @@ def _create_node_context(
manifest: Manifest,
extra_context: Dict[str, Any],
) -> Dict[str, Any]:

context = generate_runtime_model_context(node, self.config, manifest)
if isinstance(node, UnitTestNode):
context = generate_runtime_unit_test_context(node, self.config, manifest)
else:
context = generate_runtime_model_context(node, self.config, manifest)
context.update(extra_context)

if isinstance(node, GenericTestNode):
Expand Down Expand Up @@ -529,6 +540,9 @@ def compile_node(
the node's raw_code into compiled_code, and then calls the
recursive method to "prepend" the ctes.
"""
if isinstance(node, UnitTestDefinition):
return node

# Make sure Lexer for sqlparse 0.4.4 is initialized
from sqlparse.lexer import Lexer # type: ignore

Expand Down
12 changes: 12 additions & 0 deletions core/dbt/config/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ def create_project(self, rendered: RenderComponents) -> "Project":
snapshots: Dict[str, Any]
sources: Dict[str, Any]
tests: Dict[str, Any]
unit_tests: Dict[str, Any]
metrics: Dict[str, Any]
semantic_models: Dict[str, Any]
saved_queries: Dict[str, Any]
Expand All @@ -445,6 +446,7 @@ def create_project(self, rendered: RenderComponents) -> "Project":
snapshots = cfg.snapshots
sources = cfg.sources
tests = cfg.tests
unit_tests = cfg.unit_tests
metrics = cfg.metrics
semantic_models = cfg.semantic_models
saved_queries = cfg.saved_queries
Expand Down Expand Up @@ -505,6 +507,7 @@ def create_project(self, rendered: RenderComponents) -> "Project":
query_comment=query_comment,
sources=sources,
tests=tests,
unit_tests=unit_tests,
metrics=metrics,
semantic_models=semantic_models,
saved_queries=saved_queries,
Expand Down Expand Up @@ -615,6 +618,7 @@ class Project:
snapshots: Dict[str, Any]
sources: Dict[str, Any]
tests: Dict[str, Any]
unit_tests: Dict[str, Any]
metrics: Dict[str, Any]
semantic_models: Dict[str, Any]
saved_queries: Dict[str, Any]
Expand Down Expand Up @@ -648,6 +652,13 @@ def generic_test_paths(self):
generic_test_paths.append(os.path.join(test_path, "generic"))
return generic_test_paths

@property
def fixture_paths(self):
fixture_paths = []
for test_path in self.test_paths:
fixture_paths.append(os.path.join(test_path, "fixtures"))
return fixture_paths

def __str__(self):
cfg = self.to_project_config(with_packages=True)
return str(cfg)
Expand Down Expand Up @@ -693,6 +704,7 @@ def to_project_config(self, with_packages=False):
"snapshots": self.snapshots,
"sources": self.sources,
"tests": self.tests,
"unit-tests": self.unit_tests,
"metrics": self.metrics,
"semantic-models": self.semantic_models,
"saved-queries": self.saved_queries,
Expand Down
2 changes: 2 additions & 0 deletions core/dbt/config/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ def from_parts(
query_comment=project.query_comment,
sources=project.sources,
tests=project.tests,
unit_tests=project.unit_tests,
metrics=project.metrics,
semantic_models=project.semantic_models,
saved_queries=project.saved_queries,
Expand Down Expand Up @@ -324,6 +325,7 @@ def get_resource_config_paths(self) -> Dict[str, PathSet]:
"snapshots": self._get_config_paths(self.snapshots),
"sources": self._get_config_paths(self.sources),
"tests": self._get_config_paths(self.tests),
"unit_tests": self._get_config_paths(self.unit_tests),
"metrics": self._get_config_paths(self.metrics),
"semantic_models": self._get_config_paths(self.semantic_models),
"saved_queries": self._get_config_paths(self.saved_queries),
Expand Down
4 changes: 4 additions & 0 deletions core/dbt/context/context_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
model_configs = unrendered.get("saved_queries")
elif resource_type == NodeType.Exposure:
model_configs = unrendered.get("exposures")
elif resource_type == NodeType.Unit:
model_configs = unrendered.get("unit_tests")

Check warning on line 55 in core/dbt/context/context_config.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/context/context_config.py#L55

Added line #L55 was not covered by tests
else:
model_configs = unrendered.get("models")
if model_configs is None:
Expand Down Expand Up @@ -80,6 +82,8 @@
model_configs = self.project.saved_queries
elif resource_type == NodeType.Exposure:
model_configs = self.project.exposures
elif resource_type == NodeType.Unit:
model_configs = self.project.unit_tests
else:
model_configs = self.project.models
return model_configs
Expand Down
Loading