I hope for the comment from somebody with a tool or the technique on how to assert on container structures in a readable way. Here is an example I want to talk about.
from unittest.mock import ANY
import pytest
def test_chatbot_response_is_polite():
data = generate_data()
assert data == {
"id": ANY,
"confidence": pytest.approx(0.5, abs=0.5),
"text": "Hi, I am chatbot :)",
"responses": ANY.type_(list[str]), # this does not exist, but I need it :)
}
As you can see I use pytest. The thing is that sometimes I need to assert not only on specific things but also on the structure of the data (dict
, list
, tuple
, ...) with some constraints.
If I don't care about the specific field or argument of the mock call I can ignore it with the help of unittest.mock.ANY
. You can see this for the id
field. If the field is a number I can "misuse" pytest.approx()
function as for the field confidence
where I don't care about the particular value but I know it should always be in the interval (0; 1)
.
But sometimes I would like to say only, the field responses
is any list of strings. Or, it is any container with len(container) == 1
aka ANY.len(1)
. I know Nette tester has Expectations and Jest has expect.any(Number)
/expect.anything()
but is there anything like this in Python?
Also, I know there are libs like voluptuous or schema but these are not designed for these ad-hoc comparisons. And don't fit into the use-case with asserting nicely. Maybe I am wrong and I should use them in tests too.
The closest library I found is assertpy. I believe there has to be a much simpler alternative so I can use the nice plain assert
(see the tweet below) and sometimes add an escape hatch like this.
The simplest primitive I could build looks like below. I could build upon it a library, but don't even know if there is not a better way.
class ExpectPredicate:
def __init__(self, predicate: Callable[[Any], bool]):
self._predicate = predicate
def __eq__(self, other):
return self._predicate(other)
def test_chatbot_response_is_polite():
data = generate_data()
assert data == {
"id": ANY,
"confidence": pytest.approx(0.5, abs=0.5),
"text": "Hi, I am chatbot :)",
"responses": ExpectPredicate(lambda r: isinstance(r, list) and all(isinstance(i, str) for i in r)),
}
And my dream API:
from unknown_library import expect
def test_chatbot_response_is_polite():
data = generate_data()
assert data == {
"id": expect.type(str),
"confidence": expect.range(0, 1),
"text": "Hi, I am chatbot :)",
"responses": expect.type(list[str]),
}
EDIT: It seems the pytest-voluptuous does the trick and even gives me a nice error message:
from pytest_voluptuous import S
from voluptuous import All, Range
def test_chatbot_response_is_polite():
data = generate_data()
assert data == S({
"id": str,
"confidence": All(float, Range(min=0, max=1)),
"text": "Hi, I am chatbot :)",
"responses": [str],
})
Top comments (0)