Skip to content

Commit

Permalink
refactor: creates fixture test mixins that can be used outside of ERB
Browse files Browse the repository at this point in the history
Refactors the transformer test classes to split the test functionality into two:

* TransformersFixturesTestMixin -- for running the event fixture tests
* TransformersTestMixin -- for running the ERB-specific event tests

The fixtures file path constants have been moved to property methods so
they can be overrideen when used outside of ERB.
  • Loading branch information
pomegranited committed Jun 13, 2024
1 parent 5d42bf7 commit 6e383b9
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,27 @@
from django.test import TestCase

from event_routing_backends.processors.caliper.registry import CaliperTransformersRegistry
from event_routing_backends.processors.tests.transformers_test_mixin import TransformersTestMixin
from event_routing_backends.processors.tests.transformers_test_mixin import (
TransformersFixturesTestMixin,
TransformersTestMixin,
)


class TestCaliperTransformers(TransformersTestMixin, TestCase):
class CaliperTransformersFixturesTestMixin(TransformersFixturesTestMixin):
"""
Test that supported events are transformed into Caliper format correctly.
"""
EXPECTED_EVENTS_FIXTURES_PATH = '{}/fixtures/expected'.format(os.path.dirname(os.path.abspath(__file__)))
Mixin for testing Caliper event transformers.
This mixin is split into its own class so it can be used by packages outside of ERB.
"""
registry = CaliperTransformersRegistry

@property
def expected_events_fixture_path(self):
"""
Return the path to the expected transformed events fixture files.
"""
return '{}/fixtures/expected'.format(os.path.dirname(os.path.abspath(__file__)))

def assert_correct_transformer_version(self, transformed_event, transformer_version):
self.assertEqual(transformed_event['extensions']['transformerVersion'], transformer_version)

Expand All @@ -36,3 +46,9 @@ def compare_events(self, transformed_event, expected_event):
expected_event.pop('id')
transformed_event.pop('id')
self.assertDictEqual(expected_event, transformed_event)


class TestCaliperTransformers(CaliperTransformersFixturesTestMixin, TransformersTestMixin, TestCase):
"""
Test that supported events are transformed into Caliper format correctly.
"""
137 changes: 86 additions & 51 deletions event_routing_backends/processors/tests/transformers_test_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,86 +31,67 @@ class DummyTransformer(BaseTransformerMixin):
required_fields = ('does_not_exist',)


@ddt.ddt
class TransformersTestMixin:
class TransformersFixturesTestMixin:
"""
Test that supported events are transformed correctly.
Mixin to help test event transforms using "raw" and "expected" fixture data.
"""
# no limit to diff in the output of tests
maxDiff = None

registry = None
EXPECTED_EVENTS_FIXTURES_PATH = None

def setUp(self):
super().setUp()
UserFactory.create(username='edx', email='[email protected]')

@property
def raw_events_fixture_path(self):
"""
Return the path to the raw events fixture files.
"""
return f"{TEST_DIR_PATH}/fixtures/current"

@property
def expected_events_fixture_path(self):
"""
Return the path to the expected transformed events fixture files.
"""
raise NotImplementedError

def get_raw_event(self, event_filename):
"""
Return raw event json parsed from current fixtures
"""
base_event_filename = os.path.basename(event_filename)

input_event_file_path = '{test_dir}/fixtures/current/{event_filename}'.format(
test_dir=TEST_DIR_PATH, event_filename=event_filename
input_event_file_path = '{test_dir}/{event_filename}'.format(
test_dir=self.raw_events_fixture_path, event_filename=base_event_filename
)
with open(input_event_file_path, encoding='utf-8') as current:
data = json.loads(current.read())
return data

@override_settings(RUNNING_WITH_TEST_SETTINGS=True)
def test_transformer_version_with_test_settings(self):
self.registry.register('test_event')(DummyTransformer)
raw_event = self.get_raw_event('edx.course.enrollment.activated.json')
transformed_event = self.registry.get_transformer(raw_event).transform()
self.assert_correct_transformer_version(transformed_event, '[email protected]')

@override_settings(RUNNING_WITH_TEST_SETTINGS=False)
def test_transformer_version(self):
self.registry.register('test_event')(DummyTransformer)
raw_event = self.get_raw_event('edx.course.enrollment.activated.json')
transformed_event = self.registry.get_transformer(raw_event).transform()
self.assert_correct_transformer_version(transformed_event, 'event-routing-backends@{}'.format(__version__))

def test_with_no_field_transformer(self):
self.registry.register('test_event')(DummyTransformer)
with self.assertRaises(ValueError):
self.registry.get_transformer({
'name': 'test_event'
}).transform()

def test_required_field_transformer(self):
self.registry.register('test_event')(DummyTransformer)
with self.assertRaises(ValueError):
self.registry.get_transformer({
"name": "edx.course.enrollment.activated"
}).transform()

@abstractmethod
def compare_events(self, transformed_event, expected_event):
"""
Every transformer's test case will implement its own logic to test
events transformation
"""
@patch('event_routing_backends.helpers.uuid.uuid4')
@ddt.data(*EVENT_FIXTURE_FILENAMES)
def test_event_transformer(self, event_filename, mocked_uuid4):
# Used to generate the anonymized actor.name,
# which in turn is used to generate the event UUID.
mocked_uuid4.return_value = UUID('32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb')
raise NotImplementedError

# if an event's expected fixture doesn't exist, the test shouldn't fail.
# evaluate transformation of only supported event fixtures.
expected_event_file_path = '{expected_events_fixtures_path}/{event_filename}'.format(
expected_events_fixtures_path=self.EXPECTED_EVENTS_FIXTURES_PATH, event_filename=event_filename
)
def check_event_transformer(self, raw_event_file, expected_event_file):
"""
Test that given event is transformed correctly.
if not os.path.isfile(expected_event_file_path):
return
Transforms the contents of `raw_event_file` and compare it against the contents of `expected_event_file`.
original_event = self.get_raw_event(event_filename)
with open(expected_event_file_path, encoding='utf-8') as expected:
Writes errors to test_out/ for analysis.
"""
original_event = self.get_raw_event(raw_event_file)
with open(expected_event_file, encoding='utf-8') as expected:
expected_event = json.loads(expected.read())

event_filename = os.path.basename(raw_event_file)
if "anonymous" in event_filename:
with pytest.raises(ValueError):
self.registry.get_transformer(original_event).transform()
Expand All @@ -132,7 +113,61 @@ def test_event_transformer(self, event_filename, mocked_uuid4):
actual_transformed_event_file.write(",".join(out_events))
actual_transformed_event_file.write("]")

with open(f"test_output/expected.{event_filename}.json", "w") as expected_event_file:
json.dump(expected_event, expected_event_file, indent=4)
with open(f"test_output/expected.{raw_event_file}.json", "w") as test_output_file:
json.dump(expected_event, test_output_file, indent=4)

raise e


@ddt.ddt
class TransformersTestMixin:
"""
Tests that supported events are transformed correctly.
"""
def test_with_no_field_transformer(self):
self.registry.register('test_event')(DummyTransformer)
with self.assertRaises(ValueError):
self.registry.get_transformer({
'name': 'test_event'
}).transform()

def test_required_field_transformer(self):
self.registry.register('test_event')(DummyTransformer)
with self.assertRaises(ValueError):
self.registry.get_transformer({
"name": "edx.course.enrollment.activated"
}).transform()

@override_settings(RUNNING_WITH_TEST_SETTINGS=True)
def test_transformer_version_with_test_settings(self):
self.registry.register('test_event')(DummyTransformer)
raw_event = self.get_raw_event('edx.course.enrollment.activated.json')
transformed_event = self.registry.get_transformer(raw_event).transform()
self.assert_correct_transformer_version(transformed_event, '[email protected]')

@override_settings(RUNNING_WITH_TEST_SETTINGS=False)
def test_transformer_version(self):
self.registry.register('test_event')(DummyTransformer)
raw_event = self.get_raw_event('edx.course.enrollment.activated.json')
transformed_event = self.registry.get_transformer(raw_event).transform()
self.assert_correct_transformer_version(transformed_event, 'event-routing-backends@{}'.format(__version__))

@patch('event_routing_backends.helpers.uuid.uuid4')
@ddt.data(*EVENT_FIXTURE_FILENAMES)
def test_event_transformer(self, raw_event_file_path, mocked_uuid4):
# Used to generate the anonymized actor.name,
# which in turn is used to generate the event UUID.
mocked_uuid4.return_value = UUID('32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb')

# if an event's expected fixture doesn't exist, the test shouldn't fail.
# evaluate transformation of only supported event fixtures.
base_event_filename = os.path.basename(raw_event_file_path)

expected_event_file_path = '{expected_events_fixture_path}/{event_filename}'.format(
expected_events_fixture_path=self.expected_events_fixture_path, event_filename=base_event_filename
)

if not os.path.isfile(expected_event_file_path):
return

self.check_event_transformer(raw_event_file_path, expected_event_file_path)
25 changes: 21 additions & 4 deletions event_routing_backends/processors/xapi/tests/test_transformers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,30 @@
from django.test import TestCase
from django.test.utils import override_settings

from event_routing_backends.processors.tests.transformers_test_mixin import TransformersTestMixin
from event_routing_backends.processors.tests.transformers_test_mixin import (
TransformersFixturesTestMixin,
TransformersTestMixin,
)
from event_routing_backends.processors.xapi import constants
from event_routing_backends.processors.xapi.registry import XApiTransformersRegistry
from event_routing_backends.processors.xapi.transformer import XApiTransformer


class TestXApiTransformers(TransformersTestMixin, TestCase):
class XApiTransformersFixturesTestMixin(TransformersFixturesTestMixin):
"""
Test that supported events are transformed into xAPI format correctly.
Mixin for testing xAPI event transformers.
This mixin is split into its own class so it can be used by packages outside of ERB.
"""
EXPECTED_EVENTS_FIXTURES_PATH = '{}/fixtures/expected'.format(os.path.dirname(os.path.abspath(__file__)))
registry = XApiTransformersRegistry

@property
def expected_events_fixture_path(self):
"""
Return the path to the expected transformed events fixture files.
"""
return '{}/fixtures/expected'.format(os.path.dirname(os.path.abspath(__file__)))

def assert_correct_transformer_version(self, transformed_event, transformer_version):
self.assertEqual(
transformed_event.context.extensions[constants.XAPI_TRANSFORMER_VERSION_KEY],
Expand Down Expand Up @@ -68,6 +79,12 @@ def _compare_events(self, transformed_event, expected_event):
transformed_event_json = json.loads(transformed_event.to_json())
self.assertDictEqual(expected_event, transformed_event_json)


class TestXApiTransformers(XApiTransformersFixturesTestMixin, TransformersTestMixin, TestCase):
"""
Test xApi event transforms and settings.
"""

@override_settings(XAPI_AGENT_IFI_TYPE='mbox')
def test_xapi_agent_ifi_settings_mbox(self):
self.registry.register('test_event')(XApiTransformer)
Expand Down

0 comments on commit 6e383b9

Please sign in to comment.