refactoring the mixin system
Python CI / test (push) Successful in 6m46s

This commit is contained in:
2025-11-08 08:08:02 +01:00
parent 49d4e551f8
commit ea93681aaf
13 changed files with 676 additions and 350 deletions
+58
View File
@@ -0,0 +1,58 @@
"""
Simplified unit tests for abstract document elements using test mixins.
This demonstrates how test mixins can eliminate duplication and simplify tests.
"""
import unittest
from pyWebLayout.abstract.document import Document, Chapter
from tests.mixins.font_registry_tests import FontRegistryTestMixin, FontRegistryParentDelegationTestMixin
from tests.mixins.metadata_tests import MetadataContainerTestMixin
class TestDocumentFontRegistry(FontRegistryTestMixin, unittest.TestCase):
"""Test FontRegistry behavior for Document - simplified with mixin."""
def create_test_object(self):
"""Create a Document instance for testing."""
return Document("Test Document", "en-US")
class TestDocumentMetadata(MetadataContainerTestMixin, unittest.TestCase):
"""Test MetadataContainer behavior for Document - simplified with mixin."""
def create_test_object(self):
"""Create a Document instance for testing."""
return Document("Test Document", "en-US")
class TestChapterFontRegistry(FontRegistryTestMixin, unittest.TestCase):
"""Test FontRegistry behavior for Chapter - simplified with mixin."""
def create_test_object(self):
"""Create a Chapter instance for testing."""
return Chapter("Test Chapter", level=1)
class TestChapterFontRegistryParentDelegation(FontRegistryParentDelegationTestMixin, unittest.TestCase):
"""Test FontRegistry parent delegation for Chapter - simplified with mixin."""
def create_parent(self):
"""Create a Document as parent."""
return Document("Parent Document", "en-US")
def create_child(self, parent):
"""Create a Chapter with parent reference."""
return Chapter("Child Chapter", level=1, parent=parent)
class TestChapterMetadata(MetadataContainerTestMixin, unittest.TestCase):
"""Test MetadataContainer behavior for Chapter - simplified with mixin."""
def create_test_object(self):
"""Create a Chapter instance for testing."""
return Chapter("Test Chapter", level=1)
if __name__ == '__main__':
unittest.main()
View File
+116
View File
@@ -0,0 +1,116 @@
"""
Test mixins for FontRegistry functionality.
Provides reusable test cases for any class that uses the FontRegistry mixin.
"""
from pyWebLayout.style import FontWeight, FontStyle, TextDecoration
class FontRegistryTestMixin:
"""
Mixin providing standard tests for FontRegistry behavior.
Classes using this mixin must implement:
- create_test_object() -> object with FontRegistry mixin
"""
def create_test_object(self):
"""
Create an instance of the object to test.
Must be implemented by test class.
"""
raise NotImplementedError("Test class must implement create_test_object()")
def test_font_caching(self):
"""Test that fonts with identical properties are cached and reused."""
obj = self.create_test_object()
# Create font twice with same properties
font1 = obj.get_or_create_font(font_size=14, colour=(255, 0, 0), weight=FontWeight.BOLD)
font2 = obj.get_or_create_font(font_size=14, colour=(255, 0, 0), weight=FontWeight.BOLD)
# Should return the same font object (cached)
self.assertIs(font1, font2, "Fonts with identical properties should be cached")
# Should only have one font in registry
self.assertEqual(len(obj._fonts), 1, "Registry should contain exactly one font")
def test_font_differentiation(self):
"""Test that fonts with different properties create separate instances."""
obj = self.create_test_object()
# Create fonts that differ in exactly one property each
base_params = {'colour': (0, 0, 0), 'weight': FontWeight.NORMAL}
font1 = obj.get_or_create_font(font_size=14, **base_params)
font2 = obj.get_or_create_font(font_size=16, **base_params) # Different size
base_params2 = {'font_size': 18, 'weight': FontWeight.NORMAL}
font3 = obj.get_or_create_font(colour=(255, 0, 0), **base_params2) # Different color
base_params3 = {'font_size': 20, 'colour': (100, 100, 100)}
font4 = obj.get_or_create_font(weight=FontWeight.BOLD, **base_params3) # Different weight
# All should be different objects
self.assertIsNot(font1, font2, "Fonts with different sizes should be distinct")
self.assertIsNot(font1, font3, "Fonts with different colors should be distinct")
self.assertIsNot(font1, font4, "Fonts with different weights should be distinct")
self.assertIsNot(font2, font3, "Fonts should be distinct")
self.assertIsNot(font2, font4, "Fonts should be distinct")
self.assertIsNot(font3, font4, "Fonts should be distinct")
# Should have 4 fonts in registry
self.assertEqual(len(obj._fonts), 4, "Should have 4 distinct fonts")
def test_font_properties(self):
"""Test that created fonts have the correct properties."""
obj = self.create_test_object()
font = obj.get_or_create_font(
font_size=18,
colour=(128, 64, 32),
weight=FontWeight.BOLD,
style=FontStyle.ITALIC,
decoration=TextDecoration.UNDERLINE
)
self.assertEqual(font.font_size, 18)
self.assertEqual(font.colour, (128, 64, 32))
self.assertEqual(font.weight, FontWeight.BOLD)
self.assertEqual(font.style, FontStyle.ITALIC)
self.assertEqual(font.decoration, TextDecoration.UNDERLINE)
class FontRegistryParentDelegationTestMixin:
"""
Test mixin for FontRegistry parent delegation functionality.
Classes using this mixin must implement:
- create_parent() -> parent object with FontRegistry
- create_child(parent) -> child object with FontRegistry and parent link
"""
def create_parent(self):
"""Create a parent object with FontRegistry. Must be implemented."""
raise NotImplementedError("Test class must implement create_parent()")
def create_child(self, parent):
"""Create a child object with parent reference. Must be implemented."""
raise NotImplementedError("Test class must implement create_child(parent)")
def test_parent_delegation(self):
"""Test that font creation delegates to parent when available."""
parent = self.create_parent()
child = self.create_child(parent)
# Create font through child
font = child.get_or_create_font(font_size=16, colour=(200, 100, 50))
# Font should be in parent's registry, not child's
self.assertEqual(len(parent._fonts), 1, "Font should be in parent registry")
self.assertEqual(len(child._fonts), 0, "Font should NOT be in child registry")
# Getting same font through child should return parent's font
font2 = child.get_or_create_font(font_size=16, colour=(200, 100, 50))
self.assertIs(font, font2, "Child should reuse parent's font")
+68
View File
@@ -0,0 +1,68 @@
"""
Test mixins for MetadataContainer functionality.
Provides reusable test cases for any class that uses the MetadataContainer mixin.
"""
class MetadataContainerTestMixin:
"""
Mixin providing standard tests for MetadataContainer behavior.
Classes using this mixin must implement:
- create_test_object() -> object with MetadataContainer mixin
"""
def create_test_object(self):
"""
Create an instance of the object to test.
Must be implemented by test class.
"""
raise NotImplementedError("Test class must implement create_test_object()")
def test_set_and_get_metadata(self):
"""Test basic metadata storage and retrieval."""
obj = self.create_test_object()
# Set various types of metadata
obj.set_metadata("string_key", "string_value")
obj.set_metadata("int_key", 42)
obj.set_metadata("list_key", [1, 2, 3])
obj.set_metadata("dict_key", {"nested": "value"})
# Verify retrieval
self.assertEqual(obj.get_metadata("string_key"), "string_value")
self.assertEqual(obj.get_metadata("int_key"), 42)
self.assertEqual(obj.get_metadata("list_key"), [1, 2, 3])
self.assertEqual(obj.get_metadata("dict_key"), {"nested": "value"})
def test_get_nonexistent_metadata(self):
"""Test that getting nonexistent metadata returns None."""
obj = self.create_test_object()
result = obj.get_metadata("nonexistent_key")
self.assertIsNone(result, "Nonexistent metadata should return None")
def test_update_metadata(self):
"""Test that metadata values can be updated."""
obj = self.create_test_object()
# Set initial value
obj.set_metadata("key", "initial")
self.assertEqual(obj.get_metadata("key"), "initial")
# Update value
obj.set_metadata("key", "updated")
self.assertEqual(obj.get_metadata("key"), "updated", "Metadata should be updateable")
def test_metadata_isolation(self):
"""Test that metadata is isolated between instances."""
obj1 = self.create_test_object()
obj2 = self.create_test_object()
obj1.set_metadata("key", "value1")
obj2.set_metadata("key", "value2")
self.assertEqual(obj1.get_metadata("key"), "value1")
self.assertEqual(obj2.get_metadata("key"), "value2")
self.assertNotEqual(obj1.get_metadata("key"), obj2.get_metadata("key"))
View File