4

This question: Run PHPUnit Tests in Certain Order has an accepted answer which I agree with, but the design problem is with PHP and PHPUnit.

The project I'm testing uses ZF2 and Doctrine. The AbstractHttpControllerTestCase has a method "dispatch" which instantiates a ZF2 Application and goes through all the steps of creating the Response object. These tests are annotated with @covers to ensure other methods aren't covered by running requests during a test. The requests may involve view scripts which invoke view helpers which use all sorts of services, so it becomes infeasible to mock all services used during a given request (and this code would become tedious to copy and maintain for each test).

PHPUnit has the ability to run tests in a separate process, it does this by forking a new PHP instance and feeding it compiled code templates (weird stuff). It will then include all files listed by get_included_files(), which includes everything that ever hit the autoloader. Even with preserveGlobalState disabled, it will still include everything that's been touched by all prior tests in the new process.

Some of the dependencies (installed through composer) use static methods, classes marked final or both. Static methods can be mocked by PHPUnit, final classes have to be overloaded using Mockery as PHPUnit will flat out refuse to create mock objects of final classes. Overloading classes and functions (using the namespace trick) must be done in separate processes as to not influence subsequent tests. So far, so good.

Enter a test which overloads a dependency to set expectations on static methods (on a class which may or may not be marked final), or to set expectations on objects which are not yet instantiated. This will only work if none of the prior tests have ever touched the class to overload and set expectations on, or it'll fail with a "cannot redeclare class" error. PHPUnit has tried to be helpful and included everything to recreate the test environment in the subprocess, but as a result ruins the test case.

Therefore, it would be incredibly useful to mark tests with e.g. "@group isolated" and have those tests run before any other tests without having to invoke PHPUnit twice (besides the inconvenience of it, it would ruin coverage analysis).

Alternatively, if there's a way to override a class that already exists in PHP 5.5, that would allow the stricken test case to fix its precondition. But that's probably not going to happen (runkit is not an acceptable answer in any case).

Community
  • 1
  • 1
user168660
  • 41
  • 2

0 Answers0