I seem to spend 90% of my time these days writing Python tests. Which one could argue is how it should be, but whatever.
Some handy Pytest snippets I use all the time
Basic monkeypatch fixture
Monkeypatching is the thing I do most of the time. It's basically a mechanism you can use to bypass a function and replace it with a custom test function of your own.
For example, you may be doing something in your real code that calls a database to get some data. You don't want your tests to do this though, that would mean managing a test database somewhere
and that sounds like a lot of work (it is, trust me I have done it and it is terrible).
So you make sure the thing that gets the data is in a nice function all of its own - which it will be already right because single responsibility, DRY, etc etc...? - then replace it with a fake one.
This is a stupidly simple example, because why not. I have a function that generates a 12 char code, checks it is unique in the database then returns the new unique code. Yeh the code is rubbish, but that's not the point. The test is the point :D
Fixture (in conftest.py)
monkeypatch.setattr(code_generator, "check_code_unique", True)
Test code (in test_code_generator.py)
new_code = generate_code()
assert len(new_code) == 12
Actual code (in code_generator.py)
unique = False
while unique is not True:
new_code = "".join(secrets.choice(string.ascii_characters) for i in range(0, 12))
OK so I haven't actually written the
check_code_unique function yet. TDD amirite? Doesn't matter though, it's the
generate_code function we're testing so I've monkeypatched the
check_code_unique in the fixture so it will always return True.
The basic format is to create a function that returns what you need for the tests, then replace the real function with your fake test one like this:
monkeypatch.setattr([YOUR MODULE], "[YOUR FUNCTION]", [REPLACEMENT FUNCTION]). Simple right?
Basic monkeypatch fixture with parameters
The example above is fine if you always want
generate_code to return True. But wouldn't it be handy if you also tested what happened if it returned False? Of course it would, you silly sausage.
This is also super simple, but it looks fancier so will make you feel like a cooler dev, cos lambda functions are cool.
The actual code and test code are the same, we just need to change the fixture to do this:
Fixture (in conftest.py)
def mock_unique_code(monkeypatch, request):
lambda r: request.param
Same as before except we are setting some
params in the fixture and our replacement function is a bit fancier. Whatever you put in
params will run as a separate test every time the fixture is used.
This is just a bool so it is simple, but you can put a list of any old stuff in here and it will run a test case for each item in the list. Awesome huh.
The current item from the
param list is accessed via
request.param. Lambda functions look cool and complex, but really they are just tiny functions that just do one thing. TBH a lambda is not really necessary here, you could just return
request.param but just in case you need to return more items, or you need to manipulate it on some way, you can do that in here too.