Skip to content

Instantly share code, notes, and snippets.

@Potherca
Last active August 8, 2023 11:32
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Potherca/15440f045156f9244f985b5e787197db to your computer and use it in GitHub Desktop.
Save Potherca/15440f045156f9244f985b5e787197db to your computer and use it in GitHub Desktop.
Potherca's PHPUnit Best Practices
⚠️ This document is still under heavy development ⚠️

There are no "rules" other than the "rules" you set for yourself.

Discuss the game with the other players!

Simple (and specific) rules

  1. Make sure to declare a PHP constraint in the composer.json Different versions of PHP are supported by different versions of PHPUnit. Setting the PHP version makes sure a compatible PHPUnit version is required.
  2. Add any extensions to the composer.json require section To avoid tests failing because a certain extension is not installed, explicitly mention the needed extensions in the composer.json file. That way when composer install (or update) is run to fetch PHPUnit (and other dependencies), composer will report the missing extension.
  3. Add PHPUnit as a development dependency (using composer require --dev 'phpunit/phpunit')
    To make sure the a version of PHPUnit is used that can actually run a projects tests (either because the API the tests use or the PHP version that PHPUnit requires), ship a PHPUnit with a project by declaring it as development dependency.
  4. Use the PHPUnit shipped with a project, not a globally installed one.
  5. Do not mix API's from different PHPUnit versions
    Write tests for a specific version of PHPUnit.
  6. Do not commit the phpunit.xml file
    Use a phpunit.xml.dist instead
  7. Declare the PHPUnit version in the phpunit.xml.dist file.
    By declaring the correct xmlns:xsi and xsi:noNamespaceSchemaLocation, a specific version of PHPUnit XML configuration can be specified. This makes it possible for XML enabled editors to autocomplete configuration settings. This saves time looking things up in the manual.
  8. Use one assert per test method
  9. Undecided Use the static self::assert() rather than $this->assert()
    Asserts provided by PHPUnit are all static. The abstract TestCase class extends the abstract Assert class. One could even go as far as using Assert::assert()

More advanced (and more generic) rules

  1. Add separate methods to create Mocks
    When a mock is needed more than once, for the sake of readability (and to reduce code duplication) create a method that creates the mock. In extension of PHPUnit's getMock function, naming a method that creates a mock Foo should be named getMockFoo.
  2. Test structure should mirror code structure
    A class that tests Foo should be called FooTest and should live in the same namespace.

Naming conventions

  1. Test classes should be named after the class they are testing
  2. Test method names should be TestDox/AgileDox compatible
  3. Test method names should contain the subject of the test
  4. Test method names should contain the expected behaviour of the test
  5. Test method names should contain the context of the test

Method name format: function test[Subject]Should[ExpectedBehaviour]When[GivenContext]

  • It should be easy to configure tests
  • It should be easy to add or edit tests
  • It should be easy to get started (things should work out of the box)

@TODO:

  • Use the composer autoloader as test bootstrap. (special setup should not be needed for tests to run).
  • Test runner output -> Why TestDox is useful
  • Opinions on code coverage
  • Naming convention
    • Test: thingShouldExpectedResultWhenContext()
    • Dataprovider: provideDescriptionOfDataSet
  • Avoid using @test and @expectedException annotations. (Annotations should not change the way code is run. Only use for "meta" information).
  • Do not create a separate Test namespace
  • Make test stubs and use markTestIncomplete or markTestSkipped.
  • Do not use foreach in tests, use a @dataProvider instead.
  • Do not test things that are "Too simple to break" (i.e. pure getters) -> implicit tests are fine
  • Testing anti-patterns
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment