Skip to content

Commit f82fd09

Browse files
committed
Update test_mapper with improved reporting
* Exceptions in mapper init are now reported * pytest failures due to exceptions include backtrace * Fixes pytest short circuiting on mapper init
1 parent baaf907 commit f82fd09

5 files changed

Lines changed: 104 additions & 61 deletions

File tree

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
services:
22
python:
3+
container_name: rikolti
34
build:
45
context: ./
56
dockerfile: ./Dockerfile.dev

metadata_mapper/test/helpers/base_helper.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class BaseTestHelper:
3232
# Values generated for any field name in STATIC_ATTRS is cached so that it
3333
# can be reused. This is especially useful for identifier fields that need
3434
# to be referenced multiple times throughout fixture generation.
35-
STATIC_ATTRS = ["id" "identifier"]
35+
STATIC_ATTRS = ["id", "identifier"]
3636

3737
@classmethod
3838
def for_mapper(cls, module_parts: list[str]) -> type["BaseTestHelper"]:
@@ -65,7 +65,7 @@ def for_mapper(cls, module_parts: list[str]) -> type["BaseTestHelper"]:
6565

6666
def __init__(self):
6767
self.faker = Faker()
68-
self.static = []
68+
self.static = {}
6969

7070
def instantiate_record(self, record_class) -> type["Record"]:
7171
instance = record_class(self.faker.pyint, self.generate_fixture())
@@ -94,7 +94,7 @@ def generate_value_for(
9494
if isinstance(expected_type, str):
9595
return getattr(self, expected_type)()
9696
elif not skip_static and field_name in self.STATIC_ATTRS:
97-
if not self.static[field_name]:
97+
if not self.static.get(field_name):
9898
self.static[field_name] = self.generate_value_for(
9999
field_name, expected_type, skip_static=True
100100
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from datetime import datetime
2+
from random import randint
3+
from typing import Any
4+
5+
from .contentdm_helper import ContentdmTestHelper
6+
7+
class CcaVaultTestHelper(ContentdmTestHelper):
8+
9+
SCHEMA = {
10+
"contributor": "list_of_splittable_strings",
11+
"coverage": "splittable_string",
12+
"creator": "list_of_splittable_strings",
13+
"spatial": "splittable_string",
14+
"type": "list_of_splittable_strings",
15+
"language": "list_of_splittable_strings",
16+
"subject": "list_of_splittable_strings"
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from datetime import datetime
2+
from random import randint
3+
from typing import Any
4+
5+
from .contentdm_helper import ContentdmTestHelper
6+
7+
class CsudhTestHelper(ContentdmTestHelper):
8+
9+
SCHEMA = {
10+
"contributor": "list_of_splittable_strings",
11+
"coverage": "splittable_string",
12+
"creator": "list_of_splittable_strings",
13+
"spatial": "splittable_string",
14+
"type": "list_of_splittable_strings",
15+
"language": "list_of_splittable_strings",
16+
"subject": "list_of_splittable_strings"
17+
}

metadata_mapper/test/test_mapper.py

Lines changed: 66 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,85 @@
22
import os
33
import pytest
44
import re
5+
import traceback
56

67
from .helpers.base_helper import BaseTestHelper
78
from ..mappers.mapper import Record
89

9-
class TestMapper:
10-
11-
DEFAULT_TEST_METHOD_NAME = "_test_generic_mapper"
1210

13-
def find_mappers_to_test(self, start_path = "metadata_mapper/mappers"):
14-
ret = {}
11+
class TestMapper:
12+
DEFAULT_TEST_METHOD_NAME = "_test_generic_mapper"
1513

16-
for dir in os.scandir(start_path):
17-
if dir.is_dir():
18-
ret = { **ret, **self.find_mappers_to_test(dir.path) }
19-
elif dir.is_file() and dir.name.endswith("_mapper.py"):
20-
path_regex_result = re.search("([\\w\\/]+?_mapper).py", dir.path)
21-
if path_regex_result:
22-
full_mapper_path = path_regex_result[1].replace("/", ".").lstrip(".")
23-
module_parts = [p for p in full_mapper_path.split(".")
24-
if p not in ["metadata_mapper", "mappers"]]
25-
mapper_name = module_parts[-1]
26-
mapper_path = ".".join(module_parts)
14+
def find_mappers_to_test(self, start_path="metadata_mapper/mappers"):
15+
ret = {}
2716

28-
if f"test_{mapper_name}" in locals().keys():
29-
ret[mapper_path] = getattr(self, f"test_{mapper_name}")
30-
else:
31-
ret[mapper_path] = getattr(self, self.DEFAULT_TEST_METHOD_NAME)
17+
for dir in os.scandir(start_path):
18+
if dir.is_dir():
19+
ret = {**ret, **self.find_mappers_to_test(dir.path)}
20+
elif dir.is_file() and dir.name.endswith("_mapper.py"):
21+
path_regex_result = re.search("([\\w\\/]+?_mapper).py", dir.path)
22+
if path_regex_result:
23+
full_mapper_path = (
24+
path_regex_result[1].replace("/", ".").lstrip(".")
25+
)
26+
module_parts = [
27+
p
28+
for p in full_mapper_path.split(".")
29+
if p not in ["metadata_mapper", "mappers"]
30+
]
31+
mapper_name = module_parts[-1]
32+
mapper_path = ".".join(module_parts)
3233

33-
return ret
34+
if f"test_{mapper_name}" in locals().keys():
35+
ret[mapper_path] = getattr(self, f"test_{mapper_name}")
36+
else:
37+
ret[mapper_path] = getattr(self, self.DEFAULT_TEST_METHOD_NAME)
3438

35-
def get_helper(self, module_parts) -> BaseTestHelper:
36-
return BaseTestHelper.for_mapper(module_parts)
37-
38-
helper_path = f"metadata_mapper/test/helpers/{'/'.join(module_parts).replace('_mapper', '')}_helper.py"
39-
if os.path.exists(helper_path):
40-
helper_module_parts = [p.replace('_mapper', '_helper') for p in module_parts]
41-
helper_class_name = f"{self.camelize(module_parts[-1].replace('_mapper', ''))}TestHelper"
42-
helper_module = importlib.import_module(f".helpers.{'.'.join(helper_module_parts)}", package="rikolti.metadata_mapper.test")
43-
return getattr(helper_module, helper_class_name)
44-
else:
45-
return BaseTestHelper
39+
return ret
4640

47-
def get_record(self, module_parts, module) -> Record:
48-
mapper_name = module_parts[-1].replace("_mapper", "")
49-
class_name = f"{self.camelize(mapper_name)}Record"
50-
return getattr(module, class_name)
41+
def get_record(self, module_parts, module) -> Record:
42+
mapper_name = module_parts[-1].replace("_mapper", "")
43+
class_name = f"{self.camelize(mapper_name)}Record"
44+
return getattr(module, class_name)
5145

52-
def camelize(self, words: str) -> str:
53-
return "".join([word.title() for word in words.split("_")])
46+
def camelize(self, words: str) -> str:
47+
return "".join([word.title() for word in words.split("_")])
5448

55-
def _test_generic_mapper(self, record_class, helper):
56-
instance = helper.instantiate_record(record_class)
57-
try:
58-
instance.to_UCLDC()
59-
except Exception as exc:
60-
pytest.assume(False, f"{type(instance).__name__} raised {exc}")
49+
def _test_generic_mapper(self, record_class, helper):
50+
try:
51+
instance = helper.instantiate_record(record_class)
52+
try:
53+
instance.to_UCLDC()
54+
except Exception as exc:
55+
pytest.assume(
56+
False,
57+
f"{type(instance).__name__} raised '{exc}' at mapping:\n{traceback.format_exc()}",
58+
)
59+
except Exception as exc:
60+
pytest.assume(
61+
False,
62+
f"{record_class.__name__} raised '{exc}' at initialization:\n{traceback.format_exc()}",
63+
)
6164

62-
# Test methods (invoked by pytest)
65+
# Test methods (invoked by pytest)
6366

64-
# This will loop through all mappers that don't have explicit test methods and
65-
# run them with default data
66-
def test_mappers(self):
67-
default_test_method = getattr(self, self.DEFAULT_TEST_METHOD_NAME)
68-
69-
mappers = [mapper for mapper, method in self.find_mappers_to_test().items()
70-
if method == default_test_method]
67+
# This will loop through all mappers that don't have explicit test methods and
68+
# run them with default data
69+
def test_mappers(self):
70+
default_test_method = getattr(self, self.DEFAULT_TEST_METHOD_NAME)
7171

72-
for mapper in mappers:
73-
module_parts = mapper.split(".")
74-
module = importlib.import_module(f".mappers.{'.'.join(module_parts)}", package="rikolti.metadata_mapper")
75-
helper = self.get_helper(module_parts)()
76-
record_class = self.get_record(module_parts, module)
72+
mappers = [
73+
mapper
74+
for mapper, method in self.find_mappers_to_test().items()
75+
if method == default_test_method
76+
]
7777

78-
default_test_method(record_class, helper)
78+
for mapper in mappers:
79+
module_parts = mapper.split(".")
80+
module = importlib.import_module(
81+
f".mappers.{'.'.join(module_parts)}", package="rikolti.metadata_mapper"
82+
)
83+
helper = self.get_helper(module_parts)()
84+
if type(helper) != BaseTestHelper:
85+
record_class = self.get_record(module_parts, module)
86+
default_test_method(record_class, helper)

0 commit comments

Comments
 (0)