DRY vs DAMP in unit tests
Tl:Dr — DRY for production code, sometimes ok in test code, but DAMP is preferrable.
(Disclaimer: Since I use Python 3 & Django on a daily basis i’ll be using snippets from them as examples.)
Since last year up until now i’ve been struggling with a concept of DRY coding. In a nutshell, “Do not repeat yourself”. So if you can find stuff that’s pretty repetitive, then chances are you ought to automate this to a single source. This way when specification changes you can reduce having to do multiple changes.
And this is great for production code. And probably okay for test code right?
Well, theoretically (i thought) yes. We just don’t want to repeat whatever bad smell we can find. But as it turns out, this is not always the case and it depends on use cases. One day i ran into a failing test that spat out the below error message:
The error above is coming from this test:
This test case does what it intends to do from the docstring. But the problem here is I can’t tell which view is passing and which is failing. Adding further to the problem, my list_of_views param looks like a list of dictionaries containing urls and status codes we expect them to have:
Actually, the problem is that this subTest wasn’t written correctly. And it might be hard to spot too. Because this:
should be written like this:
But even if we can find the problem and approach using DRY, it doesn’t mean our tests will be readable even. Consider this as a possibility:
Looks pretty intimidating right? This was also reformatted using Flake8 so that it becomes readable. But even still, this is a lot to take in.
Each URL for each view also accepts arguments in their paths so it could look something like https://example.com/view_1/foo=foo&bar=bar
I asked my boss during a 1:1 meeting about my struggle with this. He suggested the DAMP approach. In a nutshell, use DRY for production code but DAMP is actually ok in unit tests. Because it doesn’t affect your code base at all and you really just want to test something like our views while also making it readable and easier to debug.
With this way, you can tell which view is failing and which is passing
So when is DRY acceptable in unit tests?
I’m going to take this example from here because it perfectly demonstrates (in my opinion) when DRY is good.
It’s better to use DAMP for stuff like testing endpoints & views. But for small functions, utility methods that require you to (for example) loop through a small collection of valid/invalid data types then yeah that seems reasonable.
Because with this snippet above your test result looks like this:
Lemmi know. I’m actively learning new ways to improve the quality of my code for the convenience of my peers also and anytime I can be proven wrong is a big win for me too.