How I Keep My Django API Tests Clean Using `subTest()`

I'm currently learning backend development and building a freelancer platform using Python and the Django REST Framework.
A few days ago, I was faced with a task: users should be able to filter offers based on various criteria such as the creator's ID, the minimum price or the delivery time.
As a developer, you naturally also have to test what happens when someone enters invalid values into the filters (e.g. text instead of numbers or negative numbers). In these cases, the backend should return an HTTP 400 Bad Request.
My Thought Process
My first thought was: "Am I really going to write an almost identical test function for every single invalid query parameter for each filter?"
That felt completely wrong, because in programming there's the DRY principle (Don't Repeat Yourself). My approach was the exact opposite.
If instead I put all the values into a normal loop, I run into the next problem: as soon as the first invalid value is found, the entire test stops immediately. That way, I don't find out whether the other values would have worked.
The Solution: self.subTest()
After doing a little research, I came across self.subTest() in Python. This allows you to iterate through a list of test data. If a test case fails, Python logs the error but continues running and tests the remaining parameters.
Here's an example from my backend:
def test_list_offers_invalid_filter_parameters(self):
url = reverse("offer-list")
invalid_cases = [
("creator_id", "abc"),
("creator_id", 0),
("min_price", "abc"),
("min_price", -10),
("max_delivery_time", "abc"),
("max_delivery_time", 0),
("max_delivery_time", -5),
("max_delivery_time", 12.5),
]
for param, value in invalid_cases:
with self.subTest(param=param, value=value):
response = self.client.get(url, {param: value})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
Why this is cleaner
It follows the DRY principle: the actual test code appears only once.
Easy to extend: if I add a new filter tomorrow, I don't have to write a completely new test, I just add a new line to my
invalid_cases.Clear error messages: if something goes wrong, the terminal tells me exactly which value caused the problem.
My Conclusion
Even when writing tests, it's worth taking a moment to pause and look for clean solutions. subTest() has shown me that test code can be written just as elegantly and maintanably as the actual code.
About the author
- I'm a Full-Stack Web Developer and enthusiastic nature photographer from Switzerland.
Comments
Join the discussionNo comments yet. Be the first to comment.




