-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ feat: implement value object simple class and its unit tests
- Loading branch information
1 parent
1d26a9a
commit d299096
Showing
6 changed files
with
219 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
""" | ||
Test value object module. | ||
""" | ||
|
||
from object_mother_pattern.mothers import IntegerMother | ||
from pytest import mark, raises as assert_raises | ||
|
||
from value_object_pattern import ValueObject | ||
|
||
|
||
class IntegerValueObject(ValueObject[int]): | ||
""" | ||
IntegerValueObject value object class. | ||
""" | ||
|
||
|
||
@mark.unit_testing | ||
def test_value_object_get_attribute() -> None: | ||
""" | ||
Test that a value object value can be accessed. | ||
""" | ||
value_object = IntegerValueObject(value=IntegerMother.create()) | ||
|
||
value_object.value # noqa: B018 | ||
|
||
|
||
@mark.unit_testing | ||
def test_value_object_get_protected_attribute() -> None: | ||
""" | ||
Test that a value object protected value can be accessed. | ||
""" | ||
value_object = IntegerValueObject(value=IntegerMother.create()) | ||
|
||
value_object._value # noqa: B018 | ||
|
||
|
||
@mark.unit_testing | ||
def test_value_object_cannot_get_unexistent_attribute() -> None: | ||
""" | ||
Test that a value object value cannot be modified after initialization. | ||
""" | ||
value_object = IntegerValueObject(value=IntegerMother.create()) | ||
|
||
with assert_raises( | ||
expected_exception=AttributeError, | ||
match=f"'{value_object.__class__.__name__}' object has no attribute 'not_existent_attribute'", | ||
): | ||
value_object.not_existent_attribute # type: ignore[attr-defined] # noqa: B018 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
""" | ||
Test value object module. | ||
""" | ||
|
||
from object_mother_pattern.mothers import IntegerMother | ||
from pytest import mark, raises as assert_raises | ||
|
||
from value_object_pattern import ValueObject | ||
|
||
|
||
class IntegerValueObject(ValueObject[int]): | ||
""" | ||
IntegerValueObject value object class. | ||
""" | ||
|
||
|
||
@mark.unit_testing | ||
def test_value_object_cannot_modify_value() -> None: | ||
""" | ||
Test that a value object value cannot be modified after initialization. | ||
""" | ||
value_object = IntegerValueObject(value=IntegerMother.create()) | ||
|
||
with assert_raises( | ||
expected_exception=AttributeError, | ||
match='Cannot modify attribute "value" of immutable instance', | ||
): | ||
value_object.value = IntegerMother.create() # type: ignore[misc] | ||
|
||
|
||
@mark.unit_testing | ||
def test_value_object_cannot_modify_protected_value() -> None: | ||
""" | ||
Test that a value object protected value cannot be modified after initialization. | ||
""" | ||
value_object = IntegerValueObject(value=IntegerMother.create()) | ||
|
||
with assert_raises( | ||
expected_exception=AttributeError, | ||
match='Cannot modify attribute "_value" of immutable instance', | ||
): | ||
value_object._value = IntegerMother.create() | ||
|
||
|
||
@mark.unit_testing | ||
def test_value_object_cannot_add_new_attribute() -> None: | ||
""" | ||
Test that cannot add a new attribute to a value object after initialization. | ||
""" | ||
value_object = IntegerValueObject(value=IntegerMother.create()) | ||
|
||
with assert_raises( | ||
expected_exception=AttributeError, | ||
match=f'{value_object.__class__.__name__} object has no attribute "new_attribute"', | ||
): | ||
value_object.new_attribute = IntegerMother.create() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
__version__ = '2025.01.02' | ||
|
||
from .models import ValueObject | ||
|
||
__all__ = ('ValueObject',) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from .value_object import ValueObject | ||
|
||
__all__ = ('ValueObject',) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
""" | ||
Value object generic type. | ||
""" | ||
|
||
from abc import ABC | ||
from typing import Generic, NoReturn, TypeVar, override | ||
|
||
T = TypeVar('T') | ||
|
||
|
||
class ValueObject(ABC, Generic[T]): | ||
""" | ||
ValueObject generic type. | ||
""" | ||
|
||
__slots__ = ('_value',) | ||
__match_args__ = ('_value',) | ||
|
||
_value: T | ||
|
||
def __init__(self, *, value: T) -> None: | ||
""" | ||
ValueObject value object constructor. | ||
Args: | ||
value (T): Value. | ||
""" | ||
object.__setattr__(self, '_value', value) | ||
|
||
@override | ||
def __repr__(self) -> str: | ||
""" | ||
Returns a detailed string representation of the value object. | ||
Returns: | ||
str: A string representation of the value object in the format 'ClassName(value=value)'. | ||
""" | ||
return f'{self.__class__.__name__}(value={self._value!s})' | ||
|
||
@override | ||
def __str__(self) -> str: | ||
""" | ||
Returns a simple string representation of the value object. | ||
Returns: | ||
str: The string representation of the value object value. | ||
""" | ||
return str(object=self._value) | ||
|
||
@override | ||
def __hash__(self) -> int: | ||
""" | ||
Returns the hash of the value object. | ||
Returns: | ||
int: Hash of the value object. | ||
""" | ||
return hash(self._value) | ||
|
||
@override | ||
def __eq__(self, other: object) -> bool: | ||
""" | ||
Check if the value object is equal to another value object. | ||
Args: | ||
other (object): Object to compare. | ||
Returns: | ||
bool: True if both objects are equal, otherwise False. | ||
""" | ||
if not isinstance(other, self.__class__): | ||
return NotImplemented | ||
|
||
return self._value == other.value | ||
|
||
@override | ||
def __setattr__(self, key: str, value: T) -> NoReturn: | ||
""" | ||
Prevents modification or addition of attributes in the value object. | ||
Args: | ||
key (str): The name of the attribute. | ||
value (T): The value to be assigned to the attribute. | ||
Raises: | ||
AttributeError: If there is an attempt to modify an existing attribute. | ||
AttributeError: If there is an attempt to add a new attribute. | ||
""" | ||
public_key = key.replace('_', '') | ||
public_slots1 = [slot.replace('_', '') for slot in self.__slots__] | ||
|
||
if key in self.__slots__: | ||
raise AttributeError(f'Cannot modify attribute "{key}" of immutable instance.') | ||
|
||
if public_key in public_slots1: | ||
raise AttributeError(f'Cannot modify attribute "{public_key}" of immutable instance.') | ||
|
||
raise AttributeError(f'{self.__class__.__name__} object has no attribute "{key}".') | ||
|
||
@property | ||
def value(self) -> T: | ||
""" | ||
Returns the value object value. | ||
Returns: | ||
T: The value object value. | ||
""" | ||
return self._value |