Practical PHP Testing Patterns: Testcase Class Per Class
Join the DZone community and get the full member experience.
Join For FreeWe have many, many tests in our suites. Each of them is a single method, starting with the 'test' prefix. But where do we put them? In several Testcase Classes.
There are several alternatives for choosing how to name the Testcase Classes and how many should be created. These patterns can be used in different parts of your suite, and today we will see the most natural one: Testcase Class per Class.
"When" is the right question
According to this pattern, you should create a Testcase Class for each class in the production code that you want to test. The tests in this class will exercise the production code class, as much as possible by maintaining an object in isolation, and inserting Test Double where necessary.
This pattern requires a clear correspondence between tests and classes: thus it is "adapt" for unit tests and less for functional and integration one. For end-to-end tests or acceptance tests written in the domain language, the simmetry is often lost.
For example, when you're testing a model class, it's likely that you can find test scenarios that works well on an object of the class in isolation. When you're testing the user interface instead, you do not have a strict correspondence between test scenarios and production classes (although a loose correspondence can be made with controllers in MVC frameworks).
Implementation
Usually the naming convention followed for this pattern on a medium/large scale is to replicate the hierarchical structure of the production code:library/ My/ CoolClass.php tests/ My/ CoolClassTest.php
CoolClassTest contains in this case a My_CoolClassTest (or My\CoolClassTest if you use namespaces) class extending PHPUnit_Framework_TestCase or a subclass of it.
This simple convention aids you in mapping from one test class to the related production one, and vice versa; even when thousands of classes are involved the mapping remain straightforwards, ans we can say it is O(1). Zend Framework 1 for example mostly uses this pattern and this convention.
If you have always used this pattern, you probably think that this is the norm. It is, but there are alternatives which trades off the simmetry of code and tests in order to build more cohesive tests (for example when a Testcase Class grows too much, it can be broken down in smaller classes which do not corresponde anymore to different production classes.)
Example
In this series, we presented mostly examples as unit tests, since they are the simplest to setup and transport to your computer for running. As so, most of the various pieces of code follows the convention of Testcase Class per Class.
Thus the sample code of today is more related to the structure of the tests than to their content. The folder contains two parallel trees of production code classes and test cases. For each class:
<?php class MyClass { }
you have a Testcase similarly named:
<?php // in reality this would be done via autoloading and bootstrap file require_once __DIR__ . '/../classes/MyClass.php'; class MyClassTest extends PHPUnit_Framework_TestCase { public function testCanInstanceAnObjectOfTheClassUnderTest() { $object = new MyClass; $this->assertTrue($object instanceof MyClass); } }
And if the class is under a hierarchy:
<?php class Component_OtherClass { }
the Testcase follows the same hierarchy in order to be found easily:
<?php // in reality this would be done via autoloading and bootstrap file require_once __DIR__ . '/../../classes/Component/OtherClass.php'; class Component_OtherClassTest extends PHPUnit_Framework_TestCase { public function testCanInstanceAnObjectOfTheClassUnderTest() { $object = new Component_OtherClass; $this->assertTrue($object instanceof Component_OtherClass); } }
You can take a look at the Github repository to see the folder structure.
Opinions expressed by DZone contributors are their own.
Trending
-
How To Check IP Addresses for Known Threats and Tor Exit Node Servers in Java
-
Auditing Tools for Kubernetes
-
The SPACE Framework for Developer Productivity
-
Logging Best Practices Revisited [Video]
Comments