teachingAssistant / tests /unit /application /dtos /test_dto_validation.py
Michael Hu
Create unit tests for application layer
acd758a
"""Unit tests for DTO validation utilities"""
import pytest
from unittest.mock import Mock
from src.application.dtos.dto_validation import (
ValidationError,
validate_dto,
validation_required,
validate_field,
validate_required,
validate_type,
validate_range,
validate_choices
)
class TestValidationError:
"""Test cases for ValidationError"""
def test_validation_error_basic(self):
"""Test basic ValidationError creation"""
error = ValidationError("Test error message")
assert str(error) == "Validation error: Test error message"
assert error.message == "Test error message"
assert error.field is None
assert error.value is None
def test_validation_error_with_field(self):
"""Test ValidationError with field information"""
error = ValidationError("Invalid value", field="test_field", value="invalid_value")
assert str(error) == "Validation error for field 'test_field': Invalid value"
assert error.message == "Invalid value"
assert error.field == "test_field"
assert error.value == "invalid_value"
def test_validation_error_without_field_but_with_value(self):
"""Test ValidationError without field but with value"""
error = ValidationError("Invalid value", value="test_value")
assert str(error) == "Validation error: Invalid value"
assert error.message == "Invalid value"
assert error.field is None
assert error.value == "test_value"
class TestValidateDto:
"""Test cases for validate_dto function"""
def test_validate_dto_success_with_validate_method(self):
"""Test successful DTO validation with _validate method"""
mock_dto = Mock()
mock_dto._validate = Mock()
result = validate_dto(mock_dto)
assert result is True
mock_dto._validate.assert_called_once()
def test_validate_dto_success_without_validate_method(self):
"""Test successful DTO validation without _validate method"""
mock_dto = Mock()
del mock_dto._validate # Remove the _validate method
result = validate_dto(mock_dto)
assert result is True
def test_validate_dto_failure_value_error(self):
"""Test DTO validation failure with ValueError"""
mock_dto = Mock()
mock_dto._validate.side_effect = ValueError("Validation failed")
with pytest.raises(ValidationError, match="Validation failed"):
validate_dto(mock_dto)
def test_validate_dto_failure_unexpected_error(self):
"""Test DTO validation failure with unexpected error"""
mock_dto = Mock()
mock_dto._validate.side_effect = RuntimeError("Unexpected error")
with pytest.raises(ValidationError, match="Validation failed: Unexpected error"):
validate_dto(mock_dto)
class TestValidationRequired:
"""Test cases for validation_required decorator"""
def test_validation_required_success(self):
"""Test validation_required decorator with successful validation"""
mock_instance = Mock()
mock_instance._validate = Mock()
@validation_required
def test_method(self):
return "success"
result = test_method(mock_instance)
assert result == "success"
mock_instance._validate.assert_called_once()
def test_validation_required_validation_error(self):
"""Test validation_required decorator with validation error"""
mock_instance = Mock()
mock_instance._validate.side_effect = ValueError("Validation failed")
@validation_required
def test_method(self):
return "success"
with pytest.raises(ValidationError, match="Validation failed"):
test_method(mock_instance)
def test_validation_required_method_error(self):
"""Test validation_required decorator with method execution error"""
mock_instance = Mock()
mock_instance._validate = Mock()
@validation_required
def test_method(self):
raise RuntimeError("Method error")
with pytest.raises(ValidationError, match="Error in test_method: Method error"):
test_method(mock_instance)
def test_validation_required_preserves_function_metadata(self):
"""Test that validation_required preserves function metadata"""
@validation_required
def test_method(self):
"""Test method docstring"""
return "success"
assert test_method.__name__ == "test_method"
assert test_method.__doc__ == "Test method docstring"
class TestValidateField:
"""Test cases for validate_field function"""
def test_validate_field_success(self):
"""Test successful field validation"""
def is_positive(value):
return value > 0
result = validate_field(5, "test_field", is_positive)
assert result == 5
def test_validate_field_failure_with_custom_message(self):
"""Test field validation failure with custom error message"""
def is_positive(value):
return value > 0
with pytest.raises(ValidationError, match="Custom error message"):
validate_field(-1, "test_field", is_positive, "Custom error message")
def test_validate_field_failure_with_default_message(self):
"""Test field validation failure with default error message"""
def is_positive(value):
return value > 0
with pytest.raises(ValidationError, match="Invalid value for field 'test_field'"):
validate_field(-1, "test_field", is_positive)
def test_validate_field_validator_exception(self):
"""Test field validation with validator raising exception"""
def failing_validator(value):
raise RuntimeError("Validator error")
with pytest.raises(ValidationError, match="Validation error for field 'test_field': Validator error"):
validate_field(5, "test_field", failing_validator)
class TestValidateRequired:
"""Test cases for validate_required function"""
def test_validate_required_success_with_value(self):
"""Test successful required validation with valid value"""
result = validate_required("test_value", "test_field")
assert result == "test_value"
def test_validate_required_success_with_zero(self):
"""Test successful required validation with zero (falsy but valid)"""
result = validate_required(0, "test_field")
assert result == 0
def test_validate_required_success_with_false(self):
"""Test successful required validation with False (falsy but valid)"""
result = validate_required(False, "test_field")
assert result is False
def test_validate_required_failure_with_none(self):
"""Test required validation failure with None"""
with pytest.raises(ValidationError, match="Field 'test_field' is required"):
validate_required(None, "test_field")
def test_validate_required_failure_with_empty_string(self):
"""Test required validation failure with empty string"""
with pytest.raises(ValidationError, match="Field 'test_field' cannot be empty"):
validate_required("", "test_field")
def test_validate_required_failure_with_empty_list(self):
"""Test required validation failure with empty list"""
with pytest.raises(ValidationError, match="Field 'test_field' cannot be empty"):
validate_required([], "test_field")
def test_validate_required_failure_with_empty_dict(self):
"""Test required validation failure with empty dict"""
with pytest.raises(ValidationError, match="Field 'test_field' cannot be empty"):
validate_required({}, "test_field")
class TestValidateType:
"""Test cases for validate_type function"""
def test_validate_type_success_single_type(self):
"""Test successful type validation with single type"""
result = validate_type("test", "test_field", str)
assert result == "test"
def test_validate_type_success_multiple_types(self):
"""Test successful type validation with multiple types"""
result1 = validate_type("test", "test_field", (str, int))
assert result1 == "test"
result2 = validate_type(123, "test_field", (str, int))
assert result2 == 123
def test_validate_type_failure_single_type(self):
"""Test type validation failure with single type"""
with pytest.raises(ValidationError, match="Field 'test_field' must be of type str, got int"):
validate_type(123, "test_field", str)
def test_validate_type_failure_multiple_types(self):
"""Test type validation failure with multiple types"""
with pytest.raises(ValidationError, match="Field 'test_field' must be of type str or int, got float"):
validate_type(1.5, "test_field", (str, int))
class TestValidateRange:
"""Test cases for validate_range function"""
def test_validate_range_success_within_range(self):
"""Test successful range validation within range"""
result = validate_range(5, "test_field", min_value=1, max_value=10)
assert result == 5
def test_validate_range_success_at_boundaries(self):
"""Test successful range validation at boundaries"""
result1 = validate_range(1, "test_field", min_value=1, max_value=10)
assert result1 == 1
result2 = validate_range(10, "test_field", min_value=1, max_value=10)
assert result2 == 10
def test_validate_range_success_only_min(self):
"""Test successful range validation with only minimum"""
result = validate_range(5, "test_field", min_value=1)
assert result == 5
def test_validate_range_success_only_max(self):
"""Test successful range validation with only maximum"""
result = validate_range(5, "test_field", max_value=10)
assert result == 5
def test_validate_range_success_no_limits(self):
"""Test successful range validation with no limits"""
result = validate_range(5, "test_field")
assert result == 5
def test_validate_range_failure_below_minimum(self):
"""Test range validation failure below minimum"""
with pytest.raises(ValidationError, match="Field 'test_field' must be >= 1, got 0"):
validate_range(0, "test_field", min_value=1, max_value=10)
def test_validate_range_failure_above_maximum(self):
"""Test range validation failure above maximum"""
with pytest.raises(ValidationError, match="Field 'test_field' must be <= 10, got 11"):
validate_range(11, "test_field", min_value=1, max_value=10)
def test_validate_range_with_float_values(self):
"""Test range validation with float values"""
result = validate_range(1.5, "test_field", min_value=1.0, max_value=2.0)
assert result == 1.5
class TestValidateChoices:
"""Test cases for validate_choices function"""
def test_validate_choices_success(self):
"""Test successful choices validation"""
choices = ["option1", "option2", "option3"]
result = validate_choices("option2", "test_field", choices)
assert result == "option2"
def test_validate_choices_failure(self):
"""Test choices validation failure"""
choices = ["option1", "option2", "option3"]
with pytest.raises(ValidationError, match="Field 'test_field' must be one of \\['option1', 'option2', 'option3'\\], got 'invalid'"):
validate_choices("invalid", "test_field", choices)
def test_validate_choices_with_different_types(self):
"""Test choices validation with different value types"""
choices = [1, 2, 3, "four"]
result1 = validate_choices(2, "test_field", choices)
assert result1 == 2
result2 = validate_choices("four", "test_field", choices)
assert result2 == "four"
def test_validate_choices_empty_choices(self):
"""Test choices validation with empty choices list"""
with pytest.raises(ValidationError, match="Field 'test_field' must be one of \\[\\], got 'value'"):
validate_choices("value", "test_field", [])