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
Changes from 1 commit
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
Prev Previous commit
Next Next commit
csv file fixtures (#9044)
* WIP

* adding tests

* fix tests

* more tests

* fix broken tests

* fix broken tests

* change to csv extension

* fix unit test yaml

* add mixed inline and file csv test

* add mixed inline and file csv test

* additional changes

* read file directly

* some cleanup and soem test fixes - wip

* fix test

* use better file searching

* fix final test

* cleanup

* use absolute path and fix tests
emmyoop authored Nov 9, 2023
commit 3b033ac108b2b7a7ca902ce1a1ac6719db776552
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"
7 changes: 7 additions & 0 deletions core/dbt/config/project.py
Original file line number Diff line number Diff line change
@@ -652,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)
58 changes: 45 additions & 13 deletions core/dbt/contracts/graph/unparsed.py
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
from io import StringIO

from dbt import deprecations
from dbt.clients.system import find_matching
from dbt.node_types import NodeType
from dbt.contracts.graph.semantic_models import (
Defaults,
@@ -781,45 +782,76 @@
class UnitTestFixture:
@property
def format(self) -> UnitTestFormat:
return UnitTestFormat.Dict

Check warning on line 785 in core/dbt/contracts/graph/unparsed.py

Codecov / codecov/patch

core/dbt/contracts/graph/unparsed.py#L785

Added line #L785 was not covered by tests

@property
def rows(self) -> Union[str, List[Dict[str, Any]]]:
return []
def rows(self) -> Optional[Union[str, List[Dict[str, Any]]]]:
return None

Check warning on line 789 in core/dbt/contracts/graph/unparsed.py

Codecov / codecov/patch

core/dbt/contracts/graph/unparsed.py#L789

Added line #L789 was not covered by tests

@property
def fixture(self) -> Optional[str]:
return None

Check warning on line 793 in core/dbt/contracts/graph/unparsed.py

Codecov / codecov/patch

core/dbt/contracts/graph/unparsed.py#L793

Added line #L793 was not covered by tests

def get_rows(self) -> List[Dict[str, Any]]:
def get_rows(self, project_root: str, paths: List[str]) -> List[Dict[str, Any]]:
if self.format == UnitTestFormat.Dict:
assert isinstance(self.rows, List)
return self.rows
elif self.format == UnitTestFormat.CSV:
assert isinstance(self.rows, str)
dummy_file = StringIO(self.rows)
reader = csv.DictReader(dummy_file)
rows = []
for row in reader:
rows.append(row)
if self.fixture is not None:
assert isinstance(self.fixture, str)
file_path = self.get_fixture_path(self.fixture, project_root, paths)
with open(file_path, newline="") as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
rows.append(row)
else: # using inline csv
assert isinstance(self.rows, str)
dummy_file = StringIO(self.rows)
reader = csv.DictReader(dummy_file)
rows = []
for row in reader:
rows.append(row)
return rows

def get_fixture_path(self, fixture: str, project_root: str, paths: List[str]) -> str:
fixture_path = f"{fixture}.csv"
matches = find_matching(project_root, paths, fixture_path)
if len(matches) == 0:
raise ParsingError(f"Could not find fixture file {fixture} for unit test")
elif len(matches) > 1:
raise ParsingError(
f"Found multiple fixture files named {fixture} at {[d['relative_path'] for d in matches]}. Please use a unique name for each fixture file."
)

return matches[0]["absolute_path"]

def validate_fixture(self, fixture_type, test_name) -> None:
if (self.format == UnitTestFormat.Dict and not isinstance(self.rows, list)) or (
self.format == UnitTestFormat.CSV and not isinstance(self.rows, str)
):
if self.format == UnitTestFormat.Dict and not isinstance(self.rows, list):
raise ParsingError(

Check warning on line 831 in core/dbt/contracts/graph/unparsed.py

Codecov / codecov/patch

core/dbt/contracts/graph/unparsed.py#L831

Added line #L831 was not covered by tests
f"Unit test {test_name} has {fixture_type} rows which do not match format {self.format}"
)
if self.format == UnitTestFormat.CSV and not (
isinstance(self.rows, str) or isinstance(self.fixture, str)
):
raise ParsingError(
f"Unit test {test_name} has {fixture_type} rows or fixtures which do not match format {self.format}. Expected string."
)


@dataclass
class UnitTestInputFixture(dbtClassMixin, UnitTestFixture):
input: str
rows: Union[str, List[Dict[str, Any]]] = ""
rows: Optional[Union[str, List[Dict[str, Any]]]] = None
format: UnitTestFormat = UnitTestFormat.Dict
fixture: Optional[str] = None


@dataclass
class UnitTestOutputFixture(dbtClassMixin, UnitTestFixture):
rows: Union[str, List[Dict[str, Any]]] = ""
rows: Optional[Union[str, List[Dict[str, Any]]]] = None
format: UnitTestFormat = UnitTestFormat.Dict
fixture: Optional[str] = None


@dataclass
10 changes: 8 additions & 2 deletions core/dbt/parser/unit_tests.py
Original file line number Diff line number Diff line change
@@ -67,7 +67,10 @@
original_file_path=test_case.original_file_path,
unique_id=test_case.unique_id,
config=UnitTestNodeConfig(
materialized="unit", expected_rows=test_case.expect.get_rows()
materialized="unit",
expected_rows=test_case.expect.get_rows(
self.root_project.project_root, self.root_project.fixture_paths
),
),
raw_code=tested_node.raw_code,
database=tested_node.database,
@@ -113,7 +116,7 @@
original_input_node.resource_type == NodeType.Model
and original_input_node.config.contract.enforced
):
original_input_node_columns = {

Check warning on line 119 in core/dbt/parser/unit_tests.py

Codecov / codecov/patch

core/dbt/parser/unit_tests.py#L119

Added line #L119 was not covered by tests
column.name: column.data_type for column in original_input_node.columns
}

@@ -122,7 +125,10 @@
input_unique_id = f"model.{package_name}.{input_name}"
input_node = ModelNode(
raw_code=self._build_fixture_raw_code(
given.get_rows(), original_input_node_columns
given.get_rows(
self.root_project.project_root, self.root_project.fixture_paths
),
original_input_node_columns,
),
resource_type=NodeType.Model,
package_name=package_name,
@@ -168,8 +174,8 @@
else:
try:
statically_parsed = py_extract_from_source(f"{{{{ {input} }}}}")
except ExtractionError:
raise InvalidUnitTestGivenInput(input=input)

Check warning on line 178 in core/dbt/parser/unit_tests.py

Codecov / codecov/patch

core/dbt/parser/unit_tests.py#L177-L178

Added lines #L177 - L178 were not covered by tests

if statically_parsed["refs"]:
for ref in statically_parsed["refs"]:
@@ -180,13 +186,13 @@
original_input_node = self.manifest.ref_lookup.find(
name, package, version, self.manifest
)
elif statically_parsed["sources"]:
input_package_name, input_source_name = statically_parsed["sources"][0]
original_input_node = self.manifest.source_lookup.find(

Check warning on line 191 in core/dbt/parser/unit_tests.py

Codecov / codecov/patch

core/dbt/parser/unit_tests.py#L189-L191

Added lines #L189 - L191 were not covered by tests
input_source_name, input_package_name, self.manifest
)
else:
raise InvalidUnitTestGivenInput(input=input)

Check warning on line 195 in core/dbt/parser/unit_tests.py

Codecov / codecov/patch

core/dbt/parser/unit_tests.py#L195

Added line #L195 was not covered by tests

return original_input_node

Loading
Loading