Unit tests are a standard part of the Loadsys workflow. Testing provides developers and our clients assurance that our code is as bug free as possible and it meets the requirements. When you first start writing tests, it is a real challenge to write tests that are able to provide valuable and accurate feedback.
Let’s take a simple example in CakePHP 3, a Table class with a custom find that returns the records matching a particular user.
where(['user_id' => $options['userId']]);
return $query;
}
?>
You may wind up with a lot of code following this type of pattern. Custom finders that are wrapping up a few bits of logic to find just the matching records needed. The matching test for this type of method, could look something like this.
Orders
->find('forUser', ['userId' => $userId]);
$this->assertEquals(
2,
$query->count(),
'There are only two records in the fixtures that match this user_id'
);
}
?>
See the problem with the test?
What happens when the fixture is updated? What condition are you actually verifying is being used? What gives you feedback of what went wrong when the test breaks?
The test as it stands, is tightly coupled to the fixture and relies on the fixture having only two matching records. What also ensures that the conditions for our query was properly applied, currently nothing. The more correct test is to ensure the records returned actually have the matching user_id
property set to our value. In addition, the failure message for our current test isn’t useful. A failure message should give us the ability to diagnose the reason for the failure of the test and where to look to fix itself. Our current test, in short, doesn’t add really any actual value. It will not catch actual errors in the code, and when it does fail, it will fail for reasons other than a real bug and provide feedback that won’t give us useful information regard what broke.
Let’s refactor the test to instead provide value.
Orders
->find('forUser', ['userId' => $userId]);
foreach ($query->all() as $item) {
$this->assertEquals(
$userId,
$item->userId,
'The Item `user_id` property should match our passed in `userId` value to the method, as this find selects only the records whose `user_id` is equal to the passed in `userId` property.'
);
}
}
?>
The test now actually verifies that every record returned in the query actually matches the conditions we expected. We could further improve this test to ensure for instance that we actually return at least one record, ensuring that our fixture is doing its job. We could also refine the failure message to give better information, but overall this test is already an improvement over our earlier test.
As you are going down the path of unit testing your code, remember to ensure your tests are actually testing and verifying the conditions you want to test and further that when they fail, you get plentiful information back from them.
Credit for the inspiration for this post comes from a tweet via Anthony Ferrara, which sparked a conversation in the Loadsys Slack chatroom.