Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Organizing PHPUnit Tests in Namespaces [closed]

I see two options for organizing PHPUnit unit tests into a namespace hierarchy. What are the advantages/disadvantages to these two approaches? Are there any obvious flaws I haven't considered that would make one the obvious better choice?

Consider a sample class like \SomeFramework\Utilities\AwesomeClass:

  • Approach 1: Place each TestCase class into the same namespace as the covered class.

    \SomeFramework\Utilities\AwesomeClassTest
    
    • Advantages
      • Consistent with the traditional approach to writing PHPUnit tests.
    • Disadvantages
      • Less flexibility.
      • Seems to break the principle behind using namespaces - unrelated tests are grouped into the same namespace.

  • Approach 2: Place each TestCase in a namespace named after the covered class.

    \SomeFramework\Utilities\AwesomeClass\Test
    
    • Advantages
      • Provides a very easy/obvious way to group multiple related TestCase classes together, say for different test suites.
    • Disadvantages
      • Could result in a deeper, more complex hierarchy.
like image 530
Wilco Avatar asked Aug 24 '12 22:08

Wilco


People also ask

How do I run a PHPUnit test?

How to Run Tests in PHPUnit. You can run all the tests in a directory using the PHPUnit binary installed in your vendor folder. You can also run a single test by providing the path to the test file. You use the --verbose flag to get more information on the test status.

What is a PHPUnit test?

PHPUnit is a unit testing framework for the PHP programming language. It is an instance of the xUnit design for unit testing systems that began with SUnit and became popular with JUnit. Even a small software development project usually takes hours of hard work.

Is PHPUnit a framework?

PHPUnit is a programmer-oriented testing framework for PHP. It is an instance of the xUnit architecture for unit testing frameworks. The currently supported versions are PHPUnit 9 and PHPUnit 8.


2 Answers

My proposed solution and the reasoning behind it:

Folder layout:

. ├── src │   ├── bar │   │   └── BarAwesomeClass.php │   └── foo │       └── FooAwesomeClass.php └── tests     ├── helpers     │   └── ProjectBaseTestClassWithHelperMethods.php     ├── integration     │   ├── BarModuleTest.php     │   └── FooModuleTest.php     └── unit         ├── bar         │   └── BarAwesomeClassTest.php         └── foo             └── FooAwesomeClassTest.php 

The helpers/ folder contains classes that are not tests but are only used in a testing context. Usually that folder contains a BaseTestClass maybe containing project specific helper methods and a couple of easy to reuse stub classes so you don't need as many mocks.

The integration/ folder contains tests that span over more classes and test "bigger" parts of the system. You don't have as many of them but there is no 1:1 mapping to production classes.

The unit/ folder maps 1:1 to the src/. So for every production class there is one class that contains all the unit tests for that class.

Namespaces

Approach 1: Place each TestCase class into the same namespace as the covered class.

This folder approach should solve one of your disadvantages with Approach 1. You still get the flexibility to have more tests than a pure 1:1 mapping could give you but everything is ordered and in place.

Seems to break the principle behind using namespaces - unrelated tests are grouped into the same namespace.

If the tests feel "unrelated" maybe the production code has the same issue?

It's true that the tests don't depend on one another but they might use their "close" classes as mocks or use the real ones in case of DTOs or Value Objects. So i'd say that there is a connection.

Approach 2: Place each TestCase in a namespace named after the covered class.

There are a couple of projects that do that but usually they structure it a little differently:

It's not \SomeFramework\Utilities\AwesomeClass\Test, but \SomeFramework\Tests\Utilities\AwesomeClassTest and they still keep the 1:1 mapping, but with the extra test namespace added.

Extra test namespace

My personal take is that I don't like having separate test namespaces and I'll try to find a couple for arguments for and against that choice:

Tests should serve as documentation on how to use a class

When the real class is in another namespace, the tests show how to use that class outside of its own module.

When the real class is in the same namespace, the tests show how to use that class from inside that module.

The differences are quite minor (usually a couple of "use" statements or fully-qualified paths)

When we get the possibility to say $this->getMock(AwesomeClass::CLASS) in PHP 5.5 instead of $this->getMock('\SomeFramework\Utilities\AwesomeClass') every mock will require a use statement.

For me the usage within the module is more valuable for most classes

Polluting the "Production" Namespace

When you say new \SomeFramework\Utilities\A the auto completion might show you AwesomeClass and AwesomeClassTest and some people don't want that. For external use, or when shipping your source that isn't a problem of course since the tests don't get shipped but it might be something to consider.

like image 118
edorian Avatar answered Sep 21 '22 03:09

edorian


There is a third option that I use and that fits nicely with composer autoloading: Insert a Test namespace after the first step in the hierarchy. In your case that namespace would be \SomeFramework\Tests\Utilities\ and your class would be \SomeFramework\Tests\Utilities\AwesomeClassTest.

You can then either put the tests together with the other classes in the \SomeFramework\Test directory, or put them in a separate directory. Your autoload information for composer.json could look like this:

{
    "autoload": {
        "psr-0": { 
            "SomeFramework\\": "src/",
        }
    },
    "autoload-dev": {
        "psr-0": { 
            "SomeFramework\\Tests\\": "tests/"
        }
    }
}

Advantages of the third approach are:

  • Separation of tests and production code
  • Similar folder hierarchies for tests and production classes
  • Easy autoloading
like image 44
chiborg Avatar answered Sep 23 '22 03:09

chiborg