Introduction
Doing dirty (but extremely useful) things with equals.
Documentation for version: v0.11.0
dirty-equals is a python library that (mis)uses the __eq__ method to make python code (generally unit tests)
more declarative and therefore easier to read and write.
dirty-equals can be used in whatever context you like, but it comes into its own when writing unit tests for applications where you're commonly checking the response to API calls and the contents of a database.
Usage¶
Here's a trivial example of what dirty-equals can do:
from dirty_equals import IsPositive
assert 1 == IsPositive # (1)!
assert -2 == IsPositive # this will fail! (2)
- This
assertwill pass since1is indeed positive, so the result of1 == IsPositiveisTrue. - This will fail (raise a
AssertionError) since-2is not positive, so the result of-2 == IsPositiveisFalse.
Not that interesting yet!, but consider the following unit test code using dirty-equals:
from dirty_equals import IsJson, IsNow, IsPositiveInt, IsStr
def test_user_endpoint(client: 'HttpClient', db_conn: 'Database'):
client.post('/users/create/', data=...)
user_data = db_conn.fetchrow('select * from users')
assert user_data == {
'id': IsPositiveInt, # (1)!
'username': 'samuelcolvin', # (2)!
'avatar_file': IsStr(regex=r'/[a-z0-9\-]{10}/example\.png'), # (3)!
'settings_json': IsJson({'theme': 'dark', 'language': 'en'}), # (4)!
'created_ts': IsNow(delta=3), # (5)!
}
- We don't actually care what the
idis, just that it's present, it's anintand it's positive. - We can use a normal key and value here since we know exactly what value
usernameshould have before we test it. avatar_fileis a string, but we don't know all of the string before theassert, just the format (regex) it should match.settings_jsonis aJSONstring, but it's simpler and more robust to confirm it represents a particular python object rather than compare strings.created_atis adatetime, although we don't know (or care) about its exact value; since the user was just created we know it must be close to now.deltais optional, it defaults to 2 seconds.
Without dirty-equals, you'd have to compare individual fields and/or modify some fields before comparison - the test would not be declarative or as clear.
dirty-equals can do so much more than that, for example:
IsPartialDictlets you compare a subset of a dictionaryIsStrictDictlets you confirm order in a dictionaryIsListandIsTuplelets you compare partial lists and tuples, with or without order constraints- nesting any of these types inside any others
IsInstancelets you simply confirm the type of an object- You can even use boolean operators
|and&to combine multiple conditions - and much more...
Installation¶
Simply:
dirty-equals requires Python 3.9+.