2 Testing
2.1 Fundamentals
2.1.1 Avoiding Logic in Tests
Consider the following code, which calculates the total savings (or economy) we can realize by applying a discount percentage to a list of prices:
Now, we want to write a unit test to validate this economy function. We might want to reuse the same logic as the function itself to compute the expected result in the test like this:
The test passes ✅, and everything looks good—until we deploy to production and realize there’s a bug! Can you guess what’s wrong in both the function and why the test didn’t catch it?
If we print the results of the tests with the three prices and a 10% discount, we get -17.5. Yet, as it’s an economy value, we expected a positive value from this function. The issue lies in the negative sign of our economy function:
Instead of calculating a positive discount, the logic applies a negative value.
Why didn’t we catch this bug in our tests? To calculate the expected economy, we duplicated the same flawed logic in the test itself, thus reproducing the bug in the test.
Let’s rewrite the test with a hardcoded expected value. In this case, it would become apparent that something was wrong in our code:
=== RUN TestEconomy
wrong economy: want 17.5, got -17.5
This example highlights an important principle in testing: avoid adding logic to tests, especially logic that mirrors the function under test.
A test should: