Skip to content

Commit

Permalink
cardinality and namedelement tests improved
Browse files Browse the repository at this point in the history
  • Loading branch information
pedropaulofb committed Dec 7, 2023
1 parent a8d7040 commit a7958ca
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 86 deletions.
6 changes: 4 additions & 2 deletions ontouml_py/classes/datatypes/cardinality.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ class Cardinality(BaseModel):
This class allows for the specification and validation of lower and upper bounds of cardinality,
along with properties indicating whether the elements are ordered and unique.
:ivar lower_bound: The lower bound of the cardinality, either an integer as a string or '*'. Defaults to None.
:ivar lower_bound: The lower bound of the cardinality, either a non-negative integer as a string or '*'. \
Defaults to None.
:vartype lower_bound: Optional[str]
:ivar upper_bound: The upper bound of the cardinality, either an integer as a string or '*'. Defaults to None.
:ivar upper_bound: The upper bound of the cardinality, either a non-negative integer as a string or '*'. \
Defaults to None.
:vartype upper_bound: Optional[str]
:ivar is_ordered: Flag indicating if the elements are ordered. Defaults to False.
:vartype is_ordered: bool
Expand Down
95 changes: 22 additions & 73 deletions tests/abstract_classes/test_namedelement.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def test_namedelement_type_validation() -> None:
:raises ValidationError: If the wrong type is assigned to an attribute.
"""
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be a valid set"):
Project(names="Invalid Type") # Expect set of LangString, not str


Expand All @@ -106,8 +106,8 @@ def test_namedelement_abstract_class_enforcement() -> None:
:raises TypeError: If NamedElement is instantiated directly.
"""
with pytest.raises(TypeError):
_ = NamedElement() # Abstract class should not be instantiated
with pytest.raises(TypeError, match="Can't instantiate abstract class NamedElement"):
NamedElement() # Abstract class should not be instantiated


def test_namedelement_default_values() -> None:
Expand Down Expand Up @@ -152,7 +152,7 @@ def test_namedelement_invalid_list_type_assignment() -> None:
:raises ValidationError: If invalid types are assigned to list attributes.
"""
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="type=is_instance_of"):
Project(alt_names=["Invalid"]) # Expect list of LangString, not list of str


Expand All @@ -163,7 +163,7 @@ def test_initialization_with_invalid_value_and_type(invalid_langstring: str) ->
:param invalid_langstring: A string that is not a valid LangString object.
:raises ValidationError: If an invalid value is assigned to a field expecting a LangString.
"""
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="type=is_instance_of"):
Project(names=[invalid_langstring])


Expand All @@ -173,7 +173,7 @@ def test_initialization_with_invalid_type() -> None:
:raises ValidationError: If an incorrect type is assigned to a field expecting a LangString.
"""
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be an instance of LangString"):
Project(names=[123])


Expand All @@ -195,7 +195,7 @@ def test_post_initialization_with_invalid_value(valid_langstring_list: list[Lang
:raises ValidationError: If an invalid value is assigned post-instantiation.
"""
element = Project(names=valid_langstring_list)
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be an instance of LangString"):
element.names = ["Invalid value"]


Expand All @@ -206,7 +206,7 @@ def test_post_initialization_with_invalid_type() -> None:
:raises ValidationError: If an incorrect type is assigned post-instantiation.
"""
element = Project()
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be an instance of LangString"):
element.names = [123]


Expand Down Expand Up @@ -264,9 +264,9 @@ def test_rejection_of_null_values_in_list_attributes() -> None:
:raises ValidationError: If lists with None elements are assigned.
"""
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be an instance of LangString"):
Project(alt_names=[None])
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be an instance of LangString"):
Project(editorial_notes=[None])


Expand Down Expand Up @@ -343,13 +343,13 @@ def test_rejection_of_invalid_data_in_list_attributes() -> None:
:raises ValidationError: If invalid data is assigned.
"""
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be an instance of LangString"):
Project(alt_names=[None])
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be an instance of LangString"):
Project(alt_names=["Invalid Type"])
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be an instance of LangString"):
Project(editorial_notes=[None])
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be an instance of LangString"):
Project(editorial_notes=["Invalid Type"])


Expand All @@ -370,20 +370,9 @@ def test_invalid_subclass_instantiation() -> None:
:raises ValueError: When an undefined subclass is instantiated.
"""
with pytest.raises(ValueError) as exc_info:
_ = InvalidSubclass()
assert "not an allowed subclass" in str(exc_info.value), "ValueError should mention subclass restriction."

with pytest.raises(ValueError, match="not an allowed subclass"):
InvalidSubclass()

def test_error_message_for_invalid_subclass() -> None:
"""Test the error message for instantiating an invalid subclass.
:raises AssertionError: If the error message does not match the expected format.
"""
with pytest.raises(ValueError) as exc_info:
_ = InvalidSubclass()
expected_msg_part = "not an allowed subclass"
assert expected_msg_part in str(exc_info.value), "Error message should indicate the subclass is not allowed."


def test_direct_instantiation_of_abstract_class() -> None:
Expand All @@ -395,19 +384,6 @@ def test_direct_instantiation_of_abstract_class() -> None:
NamedElement() # Attempt to instantiate an abstract class


def test_subclass_without_required_methods() -> None:
"""Test that a subclass missing required abstract methods cannot be instantiated.
:raises TypeError: If a subclass without required abstract methods is instantiated.
"""

class IncompleteSubclass(NamedElement):
pass

with pytest.raises(TypeError, match="Can't instantiate abstract class IncompleteSubclass"):
IncompleteSubclass() # Attempt to instantiate a subclass without implementing abstract methods


def test_subclass_with_all_required_methods() -> None:
"""Test the instantiation of a subclass that implements all required abstract methods of NamedElement.
Expand All @@ -429,7 +405,7 @@ def test_mixed_valid_invalid_langstring_in_lists() -> None:
"""
valid_langstring = LangString("Valid LangString")
invalid_langstring = "Invalid LangString"
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="type=is_instance_of"):
Project(names=[valid_langstring, invalid_langstring])


Expand Down Expand Up @@ -500,10 +476,11 @@ def test_empty_strings_in_uri_lists() -> None:
:raises ValidationError: If lists contain empty strings for URI attributes.
"""
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Empty string found in"):
Project(creators=[""], contributors=[""])



# Test with mixed case strings
@pytest.mark.parametrize("mixed_case_string", ["MixedCase", "MIXED", "mixed"])
def test_mixed_case_strings(mixed_case_string: str) -> None:
Expand Down Expand Up @@ -579,7 +556,7 @@ def test_assign_to_non_existent_attribute() -> None:
:raises AssertionError: If assigning to a non-existent attribute does not raise an AttributeError.
"""
element = Project()
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Object has no attribute 'non_existent_attribute'"):
element.non_existent_attribute = "Test Value"


Expand Down Expand Up @@ -609,36 +586,8 @@ def test_instantiation_with_unknown_arguments() -> None:
:return: None
:raises AssertionError: If instantiation with unknown arguments does not raise a TypeError.
"""
with pytest.raises(ValidationError):
_ = Project(unknown_arg="Test Value")


def test_modifying_instance_with_unknown_arguments() -> None:
"""Test modifying an instance of NamedElement with unknown arguments.
Ensures that attempting to modify an instance of NamedElement (or its subclass) with arguments that are not
defined in the class raises a TypeError.
:return: None
:raises AssertionError: If modification with unknown arguments does not raise a TypeError.
"""
element = Project()
with pytest.raises(AttributeError):
element.modify(unknown_arg="Test Value")


def test_setting_unknown_attributes_post_instantiation() -> None:
"""Test setting unknown attributes on an instance of NamedElement post-instantiation.
Verifies that attempting to set an unknown attribute on an instance of NamedElement (or its subclass) post-
instantiation raises an AttributeError.
:return: None
:raises AssertionError: If setting an unknown attribute post-instantiation does not raise an AttributeError.
"""
element = Project()
with pytest.raises(ValidationError):
element.unknown_attribute = "Test Value"
with pytest.raises(ValidationError, match="Extra inputs are not permitted"):
Project(unknown_arg="Test Value")


def test_namedelement_with_single_character_names() -> None:
Expand Down
22 changes: 11 additions & 11 deletions tests/datatypes/test_cardinality.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def test_cardinality_with_invalid_string_bounds() -> None:
:raises: ValueError: If non-integer, non-'*' string values are provided.
"""
with pytest.raises(ValueError):
with pytest.raises(ValidationError, match="Invalid cardinality bounds"):
Cardinality(lower_bound="a", upper_bound="b")


Expand All @@ -35,7 +35,7 @@ def test_cardinality_with_mixed_valid_invalid_bounds() -> None:
:raises: ValueError: If one bound is valid and the other is invalid.
"""
with pytest.raises(ValueError):
with pytest.raises(ValidationError, match="Invalid cardinality bounds"):
Cardinality(lower_bound="1", upper_bound="a")


Expand All @@ -44,7 +44,7 @@ def test_cardinality_with_star_and_integer_bounds() -> None:
:raises: ValueError: If lower bound is higher than upper bound.
"""
with pytest.raises(ValueError):
with pytest.raises(ValidationError, match="The cardinality's lower bound .* is higher than its upper bound"):
Cardinality(lower_bound="*", upper_bound="10")


Expand All @@ -61,7 +61,7 @@ def test_cardinality_with_negative_integer_bounds_error() -> None:
:raises: ValueError: If negative integer values are provided.
"""
with pytest.raises(ValueError):
with pytest.raises(ValueError, match="Invalid cardinality bounds"):
Cardinality(lower_bound="-1", upper_bound="-5")


Expand All @@ -70,7 +70,7 @@ def test_cardinality_with_empty_string_bounds_error() -> None:
:raises: ValueError: If empty string values are provided.
"""
with pytest.raises(ValueError):
with pytest.raises(ValidationError, match="Empty string provided for lower or upper bound"):
Cardinality(lower_bound="", upper_bound="")


Expand All @@ -79,7 +79,7 @@ def test_cardinality_with_whitespace_string_bounds_error() -> None:
:raises: ValueError: If whitespace string values are provided.
"""
with pytest.raises(ValueError):
with pytest.raises(ValidationError, match="Invalid cardinality bounds"):
Cardinality(lower_bound=" ", upper_bound=" ")


Expand Down Expand Up @@ -110,7 +110,7 @@ def test_cardinality_with_mixed_none_and_invalid_bound() -> None:
:raises: ValueError: If the class does not handle None and invalid bounds correctly.
"""
with pytest.raises(ValueError):
with pytest.raises(ValidationError, match="Invalid cardinality bounds"):
Cardinality(lower_bound=None, upper_bound="a")


Expand All @@ -119,7 +119,7 @@ def test_cardinality_with_non_numeric_string_bounds() -> None:
:raises: ValueError: If non-numeric, non-'*' string values are provided.
"""
with pytest.raises(ValueError):
with pytest.raises(ValidationError, match="Invalid cardinality bounds"):
Cardinality(lower_bound="abc", upper_bound="xyz")


Expand All @@ -128,7 +128,7 @@ def test_cardinality_with_invalid_type_for_lower_bound() -> None:
:raises: TypeError: If an invalid type is used for lower_bound.
"""
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be a valid string"):
Cardinality(lower_bound=[1, 2, 3], upper_bound="10")


Expand All @@ -137,7 +137,7 @@ def test_cardinality_with_invalid_type_for_upper_bound() -> None:
:raises: TypeError: If an invalid type is used for upper_bound.
"""
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be a valid string"):
Cardinality(lower_bound="1", upper_bound={"max": 10})


Expand All @@ -147,5 +147,5 @@ def test_cardinality_with_invalid_types_for_both_bounds() -> None:
:raises: TypeError: If invalid types are used for both bounds.
"""
with pytest.raises(ValidationError):
with pytest.raises(ValidationError, match="Input should be a valid string"):
Cardinality(lower_bound=(1, 2), upper_bound=3.14)

0 comments on commit a7958ca

Please sign in to comment.