Jdi na navigaci předmětu

PHPUnit - jednotkové testování v PHP

Osnova

  • Jednotkové testy
  • PHPUnit

Jednotkové testy

Problém: změnou kódu programové jednotky mohou vzniknout chyby

Řešení: jednotkové (unit) testy

  • verifikace
  • automatické (programové) testování jednotek
  • regresní testování
  • nejblíže vývojáři

Jednotka

Jednotka je nejmenší testovatelná část software (procedura, objekt …)

Terminologie (a)

Unit testy se neliší mezi různými technologiemi a jazyky

  • JUnit, PHPUnit, SimpleTest …​
  • Test fixtures (kontext)
    • sada předpokladů nebo stavy, které jsou potřeba, aby test mohl uspět
  • Test case (testovaný případ)
    • sada podmínek – testovaná jednotka pracuje správně či nikoliv
  • Test suites (sada testů)
    • sada testů, které sdílejí tentýž kontext, na pořadí nezáleží
  • Assertion (tvrzení, jednotková logická funkce)
    • funkce, metoda, makro atp., ověřuje chování testované jednotky, chyba znamená ukončení prováděného testu a indikaci chyby.

Terminologie (b)

  • method_stub
    • simuluje chování funkce
  • mock object
    • simuluje reálný objekt, kontrolovaný programátorem
    • tyto objekty netestujeme, ale ony mohou testovat naše objekty
  • fake object
    • jednodušší mock objekt - pouze implementuje stejný interface jako reálný objekt

Struktura testu

// příprava prostředí pro testy
setup();

// tělo testu
testXY();
testXZ();
...

// úklid na konci (nehledě na kladný/záporný výsledek testu)
teardown();

PHPUnit - dokumentace, instalace

PHPUnit - test

  • třída …​Test potomek TestCase
  • metoda test…​()

    use PHPUnit\Framework\TestCase;
    
    class StackTest extends TestCase {
        public function testPushAndPop() {
            $stack = array();
            $this->assertEquals(0, count($stack));
        }
    }

PHPUnit - závislosti

  • producent, konzument

    use PHPUnit\Framework\TestCase;
    
    class StackTest extends TestCase {
        public function testEmpty() {
            // ...
            return $stack;
        }
        #[\PHPUnit\Framework\Attributes\Depends("testEmpty")]
        public function testPush(array $stack) {
            // ...
            return $stack;
        }
        #[\PHPUnit\Framework\Attributes\Depends("testPush")]
        public function testPop(array $stack) {
            // ...
        }
    }

PHPUnit - zdroje dat

  • opakované volání pro různé sady dat
  • POZOR: data provider musí být statický a public

    #[\PHPUnit\Framework\Attributes\DataProvider("provider")]
    public function testAdd($a, $b, $c) {
        $this->assertEquals($c, $a + $b);
    }
    
    public static function provider() {
        return [
            [0, 0, 0],
            [0, 1, 1],
            // ...
        ];
    }

PHPUnit - výjimky

  • očekává se výjimka, pokud k ní nedojde je to chyba

    class ExceptionTest extends TestCase {
        public function testException() {
            $this->expectException(Exception::class);
        }
    }

PHPUnit - chyby

  • zachycení chyby

    class ExpectedErrorTest extends TestCase {
        public function testFailingInclude() {
            $this->expectException(PHPUnit\Framework\Error\Error::class);
            include 'not_existing_file.php';
        }
    }

PHPUnit - assertions (tvrzení)

  • (některá) tvrzení
    • assertCount()
    • assertEmpty()
    • assertEquals()
    • assertFalse()
    • assertTrue()
    • přehled

PHPUnit - fixtures

  • prostředí, stav světa
  • příprava/úklid - před každou test…​() metodou
    • setUp()
    • tearDown() - typicky dealokace zdrojů
  • sdílení zdrojů
    • setUpBeforeClass()
    • tearDownAfterClass()
  • pozor na globální a statické proměnné - mohou ovlivnit testy

PHPUnit - fixtures

class StackTest extends TestCase {
    protected $stack;
    protected function setUp() {
        $this->stack = [];
    }
    ...
}

PHPUnit - test suite

Object                              Tests
|-- Freezer                         |-- Freezer
|   |-- HashGenerator               |   |-- HashGenerator
|   |   `-- NonRecursiveSHA1.php    |   |   `-- NonRecursiveSHA1Test.php
|   |-- HashGenerator.php           |   |
|   |-- IdGenerator                 |   |-- IdGenerator
|   |   `-- UUID.php                |   |   `-- UUIDTest.php
|   |-- IdGenerator.php             |   |
|   |-- LazyProxy.php               |   |
|   |-- Storage                     |   |-- Storage
|   |   `-- CouchDB.php             |   |   `-- CouchDB
|   |                               |   |       |-- WithLazyLoadTest.php
|   |                               |   |       `-- WithoutLazyLoadTest.php
|   |-- Storage.php                 |   |-- StorageTest.php
|   `-- Util.php                    |   `-- UtilTest.php
`-- Freezer.php                     `-- FreezerTest.php

PHPUnit - test suite

<phpunit>
  <testsuites>
    <testsuite name="Object_Freezer">
      <directory>Tests</directory>
    </testsuite>
  </testsuites>
</phpunit>

PHPUnit - dvojníci

Testovaná jednotka závisí na jiných komponentách, které nelze použít v testovacím prostředí - nejsou dostupné, nejsou funkční, mají vedlejší efekty.

  • Stub - náhradní objekt se přednastavenými návratovými hodnotami (herec)
  • Mock - náhradní objekt validující chování testované jednotky (kritik)

PHPUnit - stub

class SomeClass {
    public function doSomething() {
        ...
    }
}
class StubTest extends TestCase {
    public function testStub() {
        $stub = $this->createStub(SomeClass::class);
        $stub->method('doSomething')->willReturn('foo');

        $this->assertEquals('foo', $stub->doSomething());
    }
}