Best practices for effective testingEdit on GitHub
The rule of thumb for your tests should be:
- Tests are treated as if they were the production code.
- Tests are easy to read and easy to maintain.
It is often stated that you should test the smallest possible unit of your application. On the one hand, that’s a good approach, but on the other hand, it often leads to too many tests and tests that show the small units are working, but it doesn’t show if the units work together. A valid argument could be to have integration tests to test everything together. That’s ok but leads to the issue that you cover most of your code lines more than once, which makes the test execution take longer.
Facades are a very good example. You can write tests for the model under test. Then you also need a test that covers the creational part of the model, and then you need to have tests for the Facade itself. You can do that, of course, but such an approach leads to many mocks, and probably you also cover code lines more often than required.
At Spryker, we focus first on the API tests. The API is always the entry point into the application code.
You should have a least two test cases per API method: the happy and the un-happy test case. Ideally, you cover each line of your API specification.
Only when the
Arrange section of your API test becomes too complex for all cases, you should consider smaller unit tests.
A good test starts with a good method name. You can get many opinions about what is good and what not about the method naming. We at Spryker, follow the practice to use speaking names that not only contain the method name under test.
Examples: testDoSomethingShouldReturnTrueWhen…() testDoSomethingShouldReturnFalseWhen…()
Three step test approach
On top of the method name, the test method body should follow a three-step testing approach:
- // Arrange
- // Act
- // Assert
These inline comments give the reader of your test method a clear understanding of what exactly is happening.
Small test methods
There are several ways to make your test methods small, easy to read and understand. When your
Arrange part becomes huge, you can use the tester class and helper classes. If you see too many code lines in this section, you can move the
Arrange code into the generated
Additionally, when you want to use the same code in different modules, you can use helpers as described in Using Another Helper in a Helper.
Take a look into \SprykerTest\Shared\Customer\Helper\CustomerDataHelper - this one can be re-used in many modules to give you CustomerTransfer.
Use message argument in assert* methods
assert* methods have the ability to pass a message. For example:
$this->assertTrue($result, 'Expected that the result of doSomething is "true" but "false" was returned.');
If the test failed, you will see the passed message on the console, which should make it easier to fix the broken test.
Use as less mocks as possible
Testing single units of your application is a good approach, however, it has some drawbacks as described in the Test API section. The more you mock, the less you really test. Besides, it is very common to forget to update mocks, which leads to even more issues in your code.
Of course, you can mock all the dependencies of a model under test, but, most likely, that just adds unneeded overhead to your test. In many cases, let’s say at least in the happy case, your code should work with the given dependencies. Testing exceptional cases very often requires mocking to be able to test the un-happy cases. In this case, you should also try to use as little mocks as possible to get the most coverage out of a few lines of test code.
Add your own testify module
It is advised to organize your tests as well. For a simpler test setup, you should at least add your own LocatorHelper, which is enabled in your project namespace by default.
class ProjectLocatorHelper extends LocatorHelper
* @var array
protected $config = [
'projectNamespaces' => [
// add your project namespaces here
'coreNamespaces' => [
For submitting the form