Series of Articles: Unit, Functional, and Coverage Testing

As a developer, you should always be able to trust that the code you write works as intended and that it doesn’t have any unintended effects on older, existing features. That’s what automated tests are for. You can read about how my team at punkt.de uses them in our series of articles on testing.

Es gibt keine Probleme. Nur Herausforderungen.

Christian Keuerleber
Frontend Usability ist sein Metier: mit SCSS und modernem JavaScript macht er jede Webanwendung fluffig
Reading duration: approx. 3 Minutes

The first part of the series focused on ensuring that our code consistently follows the same style, is written cleanly, and that third-party libraries are up to date. Now we want to verify that the code actually performs the function it is intended to.

Unit Tests – Small but Mighty

The first step in this process is writing unit tests, which we do primarily in the Backend for the PHP code. The tests themselves are implemented using the phpUnit tool.

The idea is to write code that calls individual methods of the class you want to test and verifies that the correct result is always returned when the arguments are set appropriately.

Here’s a small example test for a simple validator:

Here are a few ideas for unit tests:

  1. You test a class’s “public interface”: Normally , you only want to test the methods that are actually called from outside, since these ultimately reflect the functionality
  2. Dependencies—especially third-party code—are mocked: Since you want to test exactly that one function, and not a large function tree (with code you have no control over), you tell PHPUnit, “Don’t call the original dependency; instead, always return X in its place.”
  3. AAA – Arrange, Act, Assert: A test is easiest to understand when you first set everything up (Arrange), then execute everything (Act), and finally check whether the results were correct (Assert)
  4. No Logic in Tests: The logic you’re testing is contained within the methods being tested. If you write too much logic within a test, you end up needing a test for the test, and that offers too little benefit

Of course, there are exceptions to these principles; for example, years ago our team had a test that tested exactly one protected method in a class, since that method was the core of the class and all public methods simply called this protected method. This allowed us to save on testing effort. You can also overuse mocks, for example, to the point where you’re no longer testing real code but only the mocks themselves. These are pitfalls that you must constantly check for and that become relevant anew with every test.

With unit tests, we now know that the individual pieces of our code work correctly; now we need to verify that they are also assembled correctly.

Functional Testing—The Sum of Its Small Parts

In the unit tests, only a single section of code is called. We now want to verify that the code sections function correctly within the application logic, together with the Backend framework being used (in our case, TYPO3 Extbase). To this end, we write functional tests according to the relevant TYPO3 documentation.

By running the tests within a TYPO3 context, we ensure that the “surrounding environment” is initialized correctly. We can then write tests that, for example, access a (partially generated) database—in our case, we’re testing an importer.

We prepare a CSV file, call the import method within the test, and can then verify in the database that the rows match exactly what we expect—once again, we see AAA (Arrange, Act, Assert). Importers are just one example here; theoretically, you can also call scheduler tasks, controller actions, middleware, and other components.

We know what we're testing—but what exactly are we testing?

One area that our team hasn't yet implemented specifically, but plans to address in the future, is tracking code coverage. In PHPUnit, you can configure which source code files are theoretically part of the test environment, and PHPUnit then tracks which methods, lines of code, and similar elements within the tests are actually called for those files.

From this, you can generate a list showing, for example, whether you’ve developed paths within a complex method that haven’t been tested, or whether there are any untested classes. Additionally, statistics are generated that indicate, as a percentage, how much of the code has actually been tested.

Outlook

In the first article, we looked at code quality; in this part, we'll focus on the functions within the PHP classes. In the next part of our series, we'll look at how to test parts of our actual application.

Agile Testing and Process Expertise for Your Team:

Whether you’re an agency, an industrial company, or an independent development team: If you want to establish or further develop agile testing processes, we’ll support you with our expertise—whether through hands-on workshops or direct, project-based collaboration. We’ll guide you through every step—from introducing agile testing methods and optimizing existing processes to developing and implementing customized testing strategies. Feel free to reach out to us so we can work together to take your quality assurance and development processes to the next level!

Share:

More articles

Jeder Einzelne ist ein Tropfen, gemeinsam sind wir ein Meer.
Rebecca Düker, Product Owner at punkt.de
Working at punkt.de