Tests should be right next to the code

It is a common practice to put your tests apart from the actual code of the application, usually into the tests folder. In this article, I explain why I see it as a bad practice and why you should always put them right next to the code they are testing.

en 18. 9. 2018
PHP

Reasons why you should start doing it

  • It supports high cohesion principle which says that code that belongs together should be close.
  • It is easier to move code around the codebase.
  • You have a clearer overview of what is covered by tests. You can easily check that all the classes have corresponding test files.
  • It helps you to keep your names and namespaces in sync.
  • It helps you to jump between the implementation and the tests more easily (there is a handy shortcut for PHPStorm Ctrl+Shift+T that works even without this practice).
  • It forces you to organize your codebase better (it basically doubles the number of files in each folder).
  • If you are doing a code review in Github tests are not pushed to the bottom and you have them right after the implementation.
Table
├── __snapshots__
|   ├── simpleTable.html
|   ├── tableWithActions.html
|   └── tableWithCellRenderer.html
├── Cell.php
├── CellTest.php
├── Row.php
├── RowTest.php
├── Table.php
└── TableTest.php

Example of tests for the UI component.

"This means less cognitive overhead, which is always important to keep in mind when architecting an application." [1]

It's not a new idea

  • In the Javascript world, they’ve been doing it for React components for quite some time.
  • Test framework Jest guides you to use this approach by default.
  • Check sources of this article, this idea was proposed in articles a few years ago.

Adapting this into the PHP world

Composer

In composer you cannot use autoload-dev therefore, you end up with twice as big classmap. You can prevent this by:

  1. Deleting test files in your build process with some smart find command. I recommend introducing some more robust naming convention (like ClassNameTest.test.php or ClassNameTest.phpt) to prevent accidental deletion of the application's code.

  2. Using export-ignore directive in .gitattributes. Just add *.phpt export-ignore to the .gitattributes file in the root of your git repository and tests will be excluded from the zip or tar release package. You can check the example here.

Fixtures and snapshots can be sorted out into __fixtures__ or __snapshots__ directory which is placed next to the test file. General tools and helpers can be placed in separate namespace inside of your application or better: separated a dependency into its own package. Those directories can by deleted or excluded from distribuiton as suggested above.

Has it been validated in production?

Yes, we use it at Brand Embassy on quite a huge project without any problems.

Objections

  • Q: What about end-to-end/integration tests.
  • A: We put them next to the class whose API is being tested. If the application is tested as a black-box via calling it's HTTP API, we keep them in the separate repository or you can have them in "good-old" tests folder as discussed here [2].

  • Q: There are too many files in one directory.
  • A: This is a symptom of badly organized code. Keep you modules simple and separate responsibilities.

If you have any comment feel free to mention me on Twitter .

Sources

  1. Where Should I Put JavaScript Unit Tests?, Tyson Cadenhead 2015
  2. Why I Recommend Unit Tests in the Src Folder, Jim Lynch 2016
  3. Should a test file be placed in the same folder as the source file?, stackoverflow.com 2017

Edits

  • Added mention of export-ignore directive in .gitattributes. (Tue Sep 25 00:53:09 2018 GMT+2)