Skip to content

Commit 86bcb03

Browse files
Add comprehensive tests for humanize.py module (#530)
* Add comprehensive tests for humanize.py module to improve coverage from 71% to 100% - Add tests for _nested_getitem error handling (KeyError, IndexError, TypeError) - Add tests for humanize_error with long error message truncation - Add tests for validate_with_humanized_errors success and failure cases - Add tests for edge cases with None data and multiple validation errors - Improve overall test coverage from 89% to 90% This addresses the missing coverage in humanize.py lines 19-22, 45, and 54-57. * Fix import sorting with isort
1 parent 4a9c8f8 commit 86bcb03

File tree

1 file changed

+151
-7
lines changed

1 file changed

+151
-7
lines changed

voluptuous/tests/tests.py

Lines changed: 151 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
import pytest
99

1010
from voluptuous import (
11-
ALLOW_EXTRA, PREVENT_EXTRA, REMOVE_EXTRA, All, AllInvalid, Any, Clamp, Coerce, Contains,
12-
ContainsInvalid, Date, Datetime, Email, EmailInvalid, Equal, ExactSequence,
13-
Exclusive, Extra, FqdnUrl, In, Inclusive, InInvalid, Invalid, IsDir, IsFile, Length,
14-
Literal, LiteralInvalid, Marker, Match, MatchInvalid, Maybe, MultipleInvalid, NotIn,
15-
NotInInvalid, Number, Object, Optional, PathExists, Range, Remove, Replace,
16-
Required, Schema, Self, SomeOf, TooManyValid, TypeInvalid, Union, Unordered, Url,
17-
UrlInvalid, raises, validate,
11+
ALLOW_EXTRA, PREVENT_EXTRA, REMOVE_EXTRA, All, AllInvalid, Any, Clamp, Coerce,
12+
Contains, ContainsInvalid, Date, Datetime, Email, EmailInvalid, Equal,
13+
ExactSequence, Exclusive, Extra, FqdnUrl, In, Inclusive, InInvalid, Invalid, IsDir,
14+
IsFile, Length, Literal, LiteralInvalid, Marker, Match, MatchInvalid, Maybe,
15+
MultipleInvalid, NotIn, NotInInvalid, Number, Object, Optional, PathExists, Range,
16+
Remove, Replace, Required, Schema, Self, SomeOf, TooManyValid, TypeInvalid, Union,
17+
Unordered, Url, UrlInvalid, raises, validate,
1818
)
1919
from voluptuous.humanize import humanize_error
2020
from voluptuous.util import Capitalize, Lower, Strip, Title, Upper
@@ -1846,3 +1846,147 @@ def test_exception():
18461846
assert str(ctx.value.errors) == f"[{invalid_scalar_excp_repr}]"
18471847
ctx.value.add("Test Error")
18481848
assert str(ctx.value.errors) == f"[{invalid_scalar_excp_repr}, 'Test Error']"
1849+
1850+
1851+
# Additional tests for humanize.py module to improve coverage
1852+
def test_humanize_error_with_nested_getitem_keyerror():
1853+
"""Test _nested_getitem with KeyError (line 19-22)."""
1854+
from voluptuous.humanize import _nested_getitem
1855+
1856+
# Test KeyError handling
1857+
data = {'a': {'b': 1}}
1858+
path = ['a', 'c'] # 'c' doesn't exist in {'b': 1}
1859+
result = _nested_getitem(data, path)
1860+
assert result is None
1861+
1862+
1863+
def test_humanize_error_with_nested_getitem_indexerror():
1864+
"""Test _nested_getitem with IndexError (line 19-22)."""
1865+
from voluptuous.humanize import _nested_getitem
1866+
1867+
# Test IndexError handling
1868+
data = {'a': [1, 2, 3]}
1869+
path = ['a', 5] # Index 5 doesn't exist in [1, 2, 3]
1870+
result = _nested_getitem(data, path)
1871+
assert result is None
1872+
1873+
1874+
def test_humanize_error_with_nested_getitem_typeerror():
1875+
"""Test _nested_getitem with TypeError (line 19-22)."""
1876+
from voluptuous.humanize import _nested_getitem
1877+
1878+
# Test TypeError handling - data is not subscriptable
1879+
data = 42 # int is not subscriptable
1880+
path = ['a']
1881+
result = _nested_getitem(data, path)
1882+
assert result is None
1883+
1884+
1885+
def test_humanize_error_with_long_error_message():
1886+
"""Test humanize_error with long error message that gets truncated (line 45)."""
1887+
from voluptuous.humanize import MAX_VALIDATION_ERROR_ITEM_LENGTH, humanize_error
1888+
1889+
# Create a very long string that will be truncated
1890+
long_string = "x" * (MAX_VALIDATION_ERROR_ITEM_LENGTH + 10)
1891+
data = {'a': long_string}
1892+
schema = Schema({'a': int})
1893+
1894+
with pytest.raises(MultipleInvalid) as ctx:
1895+
schema(data)
1896+
1897+
error_message = humanize_error(data, ctx.value, max_sub_error_length=50)
1898+
assert "..." in error_message
1899+
assert len(error_message.split("Got ")[1]) <= 53 # 50 + 3 for "..."
1900+
1901+
1902+
def test_validate_with_humanized_errors_success():
1903+
"""Test validate_with_humanized_errors with successful validation (line 54-57)."""
1904+
from voluptuous.humanize import validate_with_humanized_errors
1905+
1906+
schema = Schema({'a': int, 'b': str})
1907+
data = {'a': 42, 'b': 'hello'}
1908+
1909+
result = validate_with_humanized_errors(data, schema)
1910+
assert result == data
1911+
1912+
1913+
def test_validate_with_humanized_errors_failure():
1914+
"""Test validate_with_humanized_errors with validation failure (line 54-57)."""
1915+
from voluptuous.humanize import Error, validate_with_humanized_errors
1916+
1917+
schema = Schema({'a': int, 'b': str})
1918+
data = {'a': 'not an int', 'b': 123}
1919+
1920+
with pytest.raises(Error) as ctx:
1921+
validate_with_humanized_errors(data, schema)
1922+
1923+
error_message = str(ctx.value)
1924+
assert "expected int for dictionary value @ data['a']" in error_message
1925+
assert "expected str for dictionary value @ data['b']" in error_message
1926+
assert "Got 'not an int'" in error_message
1927+
assert "Got 123" in error_message
1928+
1929+
1930+
def test_validate_with_humanized_errors_custom_max_length():
1931+
"""Test validate_with_humanized_errors with custom max_sub_error_length."""
1932+
from voluptuous.humanize import Error, validate_with_humanized_errors
1933+
1934+
schema = Schema({'a': int})
1935+
data = {'a': 'not an int'}
1936+
1937+
with pytest.raises(Error) as ctx:
1938+
validate_with_humanized_errors(data, schema, max_sub_error_length=10)
1939+
1940+
error_message = str(ctx.value)
1941+
assert "..." in error_message # Should be truncated
1942+
1943+
1944+
def test_humanize_error_with_multiple_invalid():
1945+
"""Test humanize_error with MultipleInvalid containing multiple errors."""
1946+
from voluptuous.humanize import humanize_error
1947+
1948+
schema = Schema({'a': int, 'b': str, 'c': [int]})
1949+
data = {'a': 'not an int', 'b': 123, 'c': ['not an int']}
1950+
1951+
with pytest.raises(MultipleInvalid) as ctx:
1952+
schema(data)
1953+
1954+
error_message = humanize_error(data, ctx.value)
1955+
# Should contain all three error messages
1956+
assert "expected int for dictionary value @ data['a']" in error_message
1957+
assert "expected str for dictionary value @ data['b']" in error_message
1958+
assert "expected int @ data['c'][0]" in error_message
1959+
1960+
1961+
def test_humanize_error_with_single_invalid():
1962+
"""Test humanize_error with single Invalid error."""
1963+
from voluptuous.humanize import humanize_error
1964+
1965+
schema = Schema({'a': int})
1966+
data = {'a': 'not an int'}
1967+
1968+
with pytest.raises(MultipleInvalid) as ctx:
1969+
schema(data)
1970+
1971+
error_message = humanize_error(data, ctx.value)
1972+
assert "expected int for dictionary value @ data['a']" in error_message
1973+
assert "Got 'not an int'" in error_message
1974+
1975+
1976+
def test_humanize_error_with_none_data():
1977+
"""Test humanize_error with None data."""
1978+
from voluptuous.humanize import _nested_getitem, humanize_error
1979+
1980+
# Test _nested_getitem with None data
1981+
result = _nested_getitem(None, ['a'])
1982+
assert result is None
1983+
1984+
# Test humanize_error with None data
1985+
schema = Schema({'a': int})
1986+
data = None
1987+
1988+
with pytest.raises(MultipleInvalid) as ctx:
1989+
schema(data)
1990+
1991+
error_message = humanize_error(data, ctx.value)
1992+
assert "expected a dictionary" in error_message

0 commit comments

Comments
 (0)