Upgrade to Pro — share decks privately, control downloads, hide ads and more …

My Top 10 PHPUnit Tips And Tricks

My Top 10 PHPUnit Tips And Tricks

Presented on February 26 2020 at the Confoo conference, Montreal, Canada.
https://confoo.ca/en/yul2020/session/my-top-10-phpunit-tips-tricks
---------------------------------------------------------------
Of course you test your code... you may even use test driven development.
But are your tests actually testing your code ? Or are they just there to satisfy the CI process ?

In this talk Juliette will focus on the tips and tricks she learned over the years.

Learn how to improve your test setup, what assertions to use when, why data providers are incredibly useful and how to make your tests more descriptive and easier to debug.
---------------------------------------------------------------

Links:
Code used in the presentation:
https://github.com/jrfnl/top-10-phpunit-tips-tricks-demo

PHPUnit Docs:
https://phpunit.readthedocs.io/

Juliette Reinders Folmer

February 26, 2020
Tweet

More Decks by Juliette Reinders Folmer

Other Decks in Programming

Transcript

  1. My Top 10 PHPUnit Tips & Tricks Juliette Reinders Folmer

    Tweet about it: @jrf_nl #Confoo20 Olivier Issaly
  2. Have the basic setup in place [1] In composer.json: {

    "require-dev" : { "phpunit/phpunit": "^8.0 || ^9.0" }, "autoload": { "classmap": ["src/"] }, "autoload-dev": { "classmap": ["tests/"] } }
  3. Have the basic setup in place [2] In phpunit.xml.dist: <?xml

    version="1.0" encoding="UTF-8"?> <phpunit bootstrap="./vendor/autoload.php" colors="true" > <testsuites> <testsuite name="Foo"> <directory suffix="Test.php"> ./tests/ </directory> </testsuite> </testsuites> </phpunit>
  4. Start Small (but start somewhere) <?php namespace PHPUnit_Demo; class Foo

    { static function stripQuotes($string) { return preg_replace( '`^([\'"])(.*)\1$`Ds', '$2', $string ); } }
  5. Start Small (but start somewhere) <?php namespace PHPUnit_Demo\Tests; use PHPUnit\Framework\TestCase;

    use PHPUnit_Demo\Foo; class FooTest extends TestCase { public function testStripQuotes() { $result = Foo::stripQuotes('"text"'); $this->assertEquals('text', $result); } }
  6. Use the Right Assertion <?php namespace PHPUnit_Demo\Tests; use PHPUnit\Framework\TestCase; use

    PHPUnit_Demo\Foo; class FooTest extends TestCase { public function testStripQuotes() { $result = Foo::stripQuotes('"text"'); $this->assertEquals('text', $result); $this->assertSame('text', $result); } }
  7. <?php public function testStripQuotes() { $result = Foo::stripQuotes('"some text"'); $this->assertSame('some

    text', $result); $result = Foo::stripQuotes("some 'text'"); $this->assertSame("some 'text'", $result); $result = Foo::stripQuotes(false); $this->assertSame('', $result); } Don't Just Test the Happy Path
  8. <?php public function testStripQuotes() { $result = Foo::stripQuotes('"some text"'); $this->assertSame('some

    text', $result, 'stripping quotes failed'); $result = Foo::stripQuotes("some 'text'"); $this->assertSame("some 'text'", $result, 'failed with quotes in string'); $result = Foo::stripQuotes(false); $this->assertSame('', $result, 'failed with non-string input'); } Use Fail Messages
  9. Test Method Data Provider /** * Test Foo::stripQuotes(). * *

    @dataProvider dataStripQuotes * * @param string $in * @param string $out * * @return void */ public function testStripQ($in, $out) { $result = Foo::stripQuotes($in); $this->assertSame($out, $result); } /** * Data provider. * * @return array[] */ public function dataStripQuotes() { return [ ['"some text"', 'some text'], ["some 'text'", "some 'text'"], [false, ''], ]; }
  10. Named Test Cases /** * Data provider for the testStripQuotes()

    test. * * @return array[] */ public function dataStripQuotes() { return [ 'double quoted text' => ['"some text"', 'some text'], 'quotes with quoted text within' => ["some 'text'", "some 'text'"], 'not string input - bool' => [false, ''], ]; }
  11. Filter Options Filter Shortcuts --filter TestNamespace --filter TestClass --filter testMethod

    --filter 'TestNS\\TClass::testMethod' --filter 'TestNS\\TestClass' --filter '/::testMethod .*"name"/' --filter '/::testMethod .*#5$/' --filter '/::testMethod .*#(1|2|5)$/' --filter 'testMethod#5' --filter 'testMethod#4-6' --filter '#1' --filter '#1-3' --filter 'testMethod@named test case' --filter 'testMethod@name.*case' --filter '@named test case' --filter '@named.*case'
  12. <?php namespace PHPUnit_Demo; class Foo { static function stripQuotes($string) {

    return preg_replace( '`^([\'"])(.*)\1$`Ds', '$2', $string ); } }
  13. Enabling Code Coverage [1] <?xml version="1.0" encoding="UTF-8"?> <phpunit beStrictAboutCoversAnnotation="true" forceCoversAnnotation="true">

    <filter> <whitelist addUncoveredFilesFromWhitelist="true"> <directory suffix=".php">src</directory> </whitelist> </filter> <logging> <log type="coverage-clover" target="build/logs/clover.xml"/> </logging> </phpunit>
  14. Enabling Code Coverage [2] <?php class FooTest extends TestCase {

    /** * Test Foo::stripQuotes(). * * @dataProvider dataStripQuotes * * @covers \PHPUnit_Demo\Foo::stripQuotes */ public function testStripQuotes($in, $out) { $result = Foo::stripQuotes($input); $this->assertSame($expected, $result); }
  15. Simplify { "scripts" : { "test": [ "vendor/bin/phpunit --no-coverage" ],

    "coverage": [ "vendor/bin/phpunit" ], "coverage-local": [ "vendor/bin/phpunit --coverage- html ./build/coverage-html" ] } }
  16. public function testStripQuotes($input, $expected) { if (\version_compare(\PHP_VERSION_ID, '70300', '<')) {

    $this->markTestSkipped(); return; } $this->assertSame($expected, $result); }
  17. public function testStripQuotes($input, $expected) { if (\version_compare(\PHP_VERSION_ID, '70300', '<')) {

    $this->markTestSkipped( 'This test requires PHP 7.3.0+' ); return; } $this->assertSame($expected, $result); }
  18. /** * @requires PHP >= 7.3.0 */ public function testStripQuotes($input,

    $expected) { if (\version_compare(\PHP_VERSION_ID, '70300', '<')) { $this->markTestSkipped( 'This test requires PHP 7.3.0+' ); return; } $this->assertSame($expected, $result); }
  19. PHPUnit Support v Compatible with: 9 PHP 7.3, 7.4 (support

    ends Feb 2022) 8 PHP 7.2, 7.3, 7.4 (support ends Feb 2021) 7 PHP 7.1, 7.2, 7.3 6 PHP 7.0, 7.1, 7.2 5 PHP 5.6, 7.0, 7.1 4 PHP 5.3, 5.4, 5.5, 5.6 Seemann
  20. Cross-version Compatible Fixtures public static setupBeforeClass() : void { self::$db

    = new DBConnector(); } public static tearDownAfterClass() : void { self::$db->disconnect(); }
  21. Cross-version Compatible Fixtures /** * @beforeClass */ public static setupBeforeClass()

    : void { public static setupDBConnection() { self::$db = new DBConnector(); } /** * @afterClass */ public static tearDownAfterClass() : void { public static disconnectDB() : void { self::$db->disconnect(); unset(self::$db); }
  22. Thanks! @jrf_nl @jrfnl Any questions ? Slides: https://speakerdeck.com/jrf Code: https://github.com/jrfnl/

    top-10-phpunit-tips-tricks-demo Docs: https://phpunit.readthedocs.io/ Feedback: https://joind.in/talk/dafa1