Skip to content

Commit

Permalink
Testability in IDE and pytest (#269)
Browse files Browse the repository at this point in the history
* Fix tests for IDE config

* Migrate to pytest
  • Loading branch information
gouline authored Jul 22, 2024
1 parent 2fd386a commit 8647627
Show file tree
Hide file tree
Showing 8 changed files with 634 additions and 648 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ check: check-fmt check-imports check-lint-python check-type

test:
rm -rf tests/tmp
python3 -m unittest tests
pytest tests
.PHONY: test

pre: fix check test
Expand Down
1 change: 1 addition & 0 deletions requirements-test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ black>=23.11.0
isort>=5.12.0
pylint>=3.0.2
mypy>=1.7.1
pytest>=8.3.1
molot~=1.0.0
dbt-postgres~=1.8.1
python-dotenv~=1.0.1
Expand Down
11 changes: 5 additions & 6 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import logging

from dbtmetabase.format import setup_logging

from .test_exposures import *
from .test_format import *
from .test_manifest import *
from .test_metabase import *
from .test_models import *
from tests.test_exposures import *
from tests.test_format import *
from tests.test_manifest import *
from tests.test_metabase import *
from tests.test_models import *

setup_logging(level=logging.DEBUG, path=None)
159 changes: 81 additions & 78 deletions tests/test_exposures.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,86 @@
import unittest
from operator import itemgetter
from pathlib import Path

import pytest
import yaml

from ._mocks import FIXTURES_PATH, TMP_PATH, MockDbtMetabase


class TestExposures(unittest.TestCase):
def setUp(self):
self.c = MockDbtMetabase()
TMP_PATH.mkdir(exist_ok=True)

def _assert_exposures(self, expected_path: Path, actual_path: Path):
with open(expected_path, encoding="utf-8") as f:
expected = yaml.safe_load(f)
with open(actual_path, encoding="utf-8") as f:
actual = yaml.safe_load(f)

self.assertEqual(
sorted(expected["exposures"], key=itemgetter("name")),
actual["exposures"],
)

def test_exposures(self):
fixtures_path = FIXTURES_PATH / "exposure" / "default"
output_path = TMP_PATH / "exposure" / "default"
self.c.extract_exposures(
output_path=str(output_path),
output_grouping=None,
)

self._assert_exposures(
fixtures_path / "exposures.yml",
output_path / "exposures.yml",
)

def test_exposures_collection_grouping(self):
fixtures_path = FIXTURES_PATH / "exposure" / "collection"
output_path = TMP_PATH / "exposure" / "collection"
self.c.extract_exposures(
output_path=str(output_path),
output_grouping="collection",
)

for file in fixtures_path.iterdir():
self._assert_exposures(file, output_path / file.name)

def test_exposures_grouping_type(self):
fixtures_path = FIXTURES_PATH / "exposure" / "type"
output_path = TMP_PATH / "exposure" / "type"
self.c.extract_exposures(
output_path=str(output_path),
output_grouping="type",
)

for file in (fixtures_path / "card").iterdir():
self._assert_exposures(file, output_path / "card" / file.name)

for file in (fixtures_path / "dashboard").iterdir():
self._assert_exposures(file, output_path / "dashboard" / file.name)

def test_exposures_aliased_ref(self):
for model in self.c.manifest.read_models():
if not model.name.startswith("stg_"):
model.alias = f"{model.name}_alias"

aliases = [m.alias for m in self.c.manifest.read_models()]
self.assertIn("orders_alias", aliases)
self.assertIn("customers_alias", aliases)

fixtures_path = FIXTURES_PATH / "exposure" / "default"
output_path = TMP_PATH / "exposure" / "aliased"
self.c.extract_exposures(
output_path=str(output_path),
output_grouping=None,
)

self._assert_exposures(
fixtures_path / "exposures.yml",
output_path / "exposures.yml",
)
from tests._mocks import FIXTURES_PATH, TMP_PATH, MockDbtMetabase

TMP_PATH.mkdir(exist_ok=True)


@pytest.fixture(name="core")
def fixture_core() -> MockDbtMetabase:
return MockDbtMetabase()


def _assert_exposures(expected_path: Path, actual_path: Path):
with open(expected_path, encoding="utf-8") as f:
expected = yaml.safe_load(f)
with open(actual_path, encoding="utf-8") as f:
actual = yaml.safe_load(f)

assert actual["exposures"] == sorted(expected["exposures"], key=itemgetter("name"))


def test_exposures(core: MockDbtMetabase):
fixtures_path = FIXTURES_PATH / "exposure" / "default"
output_path = TMP_PATH / "exposure" / "default"
core.extract_exposures(
output_path=str(output_path),
output_grouping=None,
)

_assert_exposures(
fixtures_path / "exposures.yml",
output_path / "exposures.yml",
)


def test_exposures_collection_grouping(core: MockDbtMetabase):
fixtures_path = FIXTURES_PATH / "exposure" / "collection"
output_path = TMP_PATH / "exposure" / "collection"
core.extract_exposures(
output_path=str(output_path),
output_grouping="collection",
)

for file in fixtures_path.iterdir():
_assert_exposures(file, output_path / file.name)


def test_exposures_grouping_type(core: MockDbtMetabase):
fixtures_path = FIXTURES_PATH / "exposure" / "type"
output_path = TMP_PATH / "exposure" / "type"
core.extract_exposures(
output_path=str(output_path),
output_grouping="type",
)

for file in (fixtures_path / "card").iterdir():
_assert_exposures(file, output_path / "card" / file.name)

for file in (fixtures_path / "dashboard").iterdir():
_assert_exposures(file, output_path / "dashboard" / file.name)


def test_exposures_aliased_ref(core: MockDbtMetabase):
for model in core.manifest.read_models():
if not model.name.startswith("stg_"):
model.alias = f"{model.name}_alias"

aliases = [m.alias for m in core.manifest.read_models()]
assert "orders_alias" in aliases
assert "customers_alias" in aliases

fixtures_path = FIXTURES_PATH / "exposure" / "default"
output_path = TMP_PATH / "exposure" / "aliased"
core.extract_exposures(
output_path=str(output_path),
output_grouping=None,
)

_assert_exposures(
fixtures_path / "exposures.yml",
output_path / "exposures.yml",
)
144 changes: 63 additions & 81 deletions tests/test_format.py
Original file line number Diff line number Diff line change
@@ -1,91 +1,73 @@
import unittest

from dbtmetabase.format import Filter, NullValue, dump_yaml, safe_description, safe_name
from tests._mocks import FIXTURES_PATH, TMP_PATH

from ._mocks import FIXTURES_PATH, TMP_PATH

def test_filter():
assert Filter(include=("alpHa", "bRavo")).match("Alpha")
assert Filter().match("Alpha")
assert Filter().match("")
assert not Filter(include=("alpHa", "bRavo"), exclude=("alpha",)).match("Alpha")
assert not Filter(exclude=("alpha",)).match("Alpha")
assert Filter(include="alpha").match("Alpha")
assert not Filter(exclude="alpha").match("Alpha")

class TestFormat(unittest.TestCase):
def test_filter(self):
self.assertTrue(
Filter(
include=("alpHa", "bRavo"),
).match("Alpha")
)
self.assertTrue(Filter().match("Alpha"))
self.assertTrue(Filter().match(""))
self.assertFalse(
Filter(
include=("alpHa", "bRavo"),
exclude=("alpha",),
).match("Alpha")
)
self.assertFalse(
Filter(
exclude=("alpha",),
).match("Alpha")
)
self.assertTrue(Filter(include="alpha").match("Alpha"))
self.assertFalse(Filter(exclude="alpha").match("Alpha"))

def test_filter_wildcard(self):
self.assertTrue(Filter(include="stg_*").match("stg_orders"))
self.assertTrue(Filter(include="STG_*").match("stg_ORDERS"))
self.assertFalse(Filter(include="stg_*").match("orders"))
self.assertTrue(Filter(include="order?").match("orders"))
self.assertFalse(Filter(include="order?").match("ordersz"))
self.assertTrue(Filter(include="*orders", exclude="stg_*").match("_orders"))
self.assertFalse(Filter(include="*orders", exclude="stg_*").match("stg_orders"))
def test_filter_wildcard():
assert Filter(include="stg_*").match("stg_orders")
assert Filter(include="STG_*").match("stg_ORDERS")
assert not Filter(include="stg_*").match("orders")
assert Filter(include="order?").match("orders")
assert not Filter(include="order?").match("ordersz")
assert Filter(include="*orders", exclude="stg_*").match("_orders")
assert not Filter(include="*orders", exclude="stg_*").match("stg_orders")

def test_null_value(self):
self.assertIsNotNone(NullValue)
self.assertFalse(NullValue)
self.assertIs(NullValue, NullValue)

def test_safe_name(self):
self.assertEqual(
"somebody_s_2_collections_",
safe_name("Somebody's 2 collections!"),
)
self.assertEqual(
"somebody_s_2_collections_",
safe_name("somebody_s_2_collections_"),
)
self.assertEqual("", safe_name(""))
def test_null_value():
assert NullValue is not None
assert not NullValue
assert NullValue is NullValue


def test_safe_name():
assert safe_name("Somebody's 2 collections!") == "somebody_s_2_collections_"
assert safe_name("somebody_s_2_collections_") == "somebody_s_2_collections_"
assert safe_name("") == ""

def test_safe_description(self):
self.assertEqual(
"Depends on\n\nQuestion ( #2 )!",
safe_description("Depends on\n\nQuestion {{ #2 }}!"),
)
self.assertEqual(
"Depends on\n\nQuestion ( #2 )!",
safe_description("Depends on\n\nQuestion ( #2 )!"),
)
self.assertEqual(
"Depends on\n\nQuestion { #2 }!",
safe_description("Depends on\n\nQuestion { #2 }!"),
)
self.assertEqual(
"(start_date) - cast((rolling_days))",
safe_description("{{start_date}} - cast({{rolling_days}})"),
)

def test_dump_yaml(self):
fixture_path = FIXTURES_PATH / "test_dump_yaml.yml"
output_path = TMP_PATH / "test_dump_yaml.yml"
with open(output_path, "w", encoding="utf-8") as f:
dump_yaml(
data={
"root": {
"attr1": "val1\nend",
"attr2": ["val2", "val3"],
},
def test_safe_description():
assert (
safe_description("Depends on\n\nQuestion {{ #2 }}!")
== "Depends on\n\nQuestion ( #2 )!"
)
assert (
safe_description("Depends on\n\nQuestion ( #2 )!")
== "Depends on\n\nQuestion ( #2 )!"
)
assert (
safe_description("Depends on\n\nQuestion { #2 }!")
== "Depends on\n\nQuestion { #2 }!"
)
assert (
safe_description("{{start_date}} - cast({{rolling_days}})")
== "(start_date) - cast((rolling_days))"
)


def test_dump_yaml():
fixture_path = FIXTURES_PATH / "test_dump_yaml.yml"
output_path = TMP_PATH / "test_dump_yaml.yml"
with open(output_path, "w", encoding="utf-8") as f:
dump_yaml(
data={
"root": {
"attr1": "val1\nend",
"attr2": ["val2", "val3"],
},
stream=f,
)
with open(output_path, "r", encoding="utf-8") as f:
actual = f.read()
with open(fixture_path, "r", encoding="utf-8") as f:
expected = f.read()
self.assertEqual(expected, actual)
},
stream=f,
)
with open(output_path, "r", encoding="utf-8") as f:
actual = f.read()
with open(fixture_path, "r", encoding="utf-8") as f:
expected = f.read()
assert actual == expected
Loading

0 comments on commit 8647627

Please sign in to comment.