As I have become more proficient at writing tests, I have noticed one common pitfall that many developers fall into: Writing tests that repeat the logic they are testing.
For example, this blog post wants to test a simple logic test:
def activable? persisted? and (status_new? or status_suspended?) end
The blog post then goes on to describe an elaborate way to generate a logic table that can test every combination of values for those three boolean variables. That’s great, and very clever. My problem though comes with the actual test:
if persisted and (_new or suspended) license.should be_activable else license.should_not be_activable end
What this really does is runs every combination through, and validates that the test works exactly the same way as the original code. What it fails to do is validate that every case is actually correct!
Unfortunately, there is no substitute for taking the time to establish the data for test cases – not just the inputs, but also the expected outputs. I won’t write the code here, but I think I would create a CSV to input a truth table written out by hand, of the form:
persisted,new,suspended,result 0,0,0,0 0,0,1,0 ... 1,1,0,1 ...
Once the test case loads the truth table with both the inputs and expected outputs, it can truly test if the function is correct by running the code function on the inputs and comparing the result to the expected value. Now we know if the function is actually correct!