Příručka:Testování PHP kódu/Píšeme testy jednotky

This page is a translated version of the page Manual:PHP unit testing/Writing unit tests and the translation is 98% complete.

Obecná rada

Příručka PHPUnit poskytuje pokyny pro pochopení a vývoj testování jednotek. Zvláštní pozornost věnujte sekcím o testech psaní a organizování.

Vývoj osvědčených postupů

Vývojáři noví v testování jednotek v MediaWiki by měli jako výchozí bod použít SampleTest.php – obsahuje užitečné komentáře, které usnadní proces učení se jak psát testy jednotek.

Dalším zdrojem jsou slidy z přednášky PHPUnit Best Practices (Doporučené postupy PHPUnit), kterou Sebastian Bergmann přednesl na OSCON 2010.

Vývojáři by se měli vyvarovat vymýšlení nových konvencí nebo vypůjčování konvencí z jiných rámců. Použití již dobře zavedených konvencí PHPUnit poslouží k tomu, aby byly testy MediaWiki užitečné a použitelné.

Jednotkové testy by se měly řídit A Set of Unit Testing Rules (Sada pravidel testování jednotek) od Michaela Featherse.

Napište testovací kód

Zkuste, prosím, napsat testovatelný kód.

MediaWiki nebyla napsána s cílem být testovatelná. Všude používá globální proměnné a na mnoha místech statické metody. Toto je dědictví, které musíme přijmout, ale snažte se tyto věci nezavádět do nového kódu a zkuste to v budoucnu změnit.

Dobrým zdrojem může být Guide to Testability Miška Heveryho. (Miško Hevery je [jeden z?] agilních trenérů Google.)

Testovací konvence

Název souboru musí končit Test.php. Jako výchozí bod použijte SampleTest.php.

setUp() a tearDown()

  • Musí být protected funkcí.
  • tearDown() by měl být v opačném pořadí než setUp().
  • setUp() začíná voláním svého rodiče.
  • tearDown() končí voláním svého rodiče.

Rozhodovací funkce

  • Musí být public funkcí.
  • Název funkce by měl být v lowerCamelCase a začínat slovem test. Např. function testFooBar.
  • Kdykoli je to možné, odkazujte na nejdůležitější testovací metodu. Např. Html::expandAttributes je testován v HtmlTest::testExpandAttributes.

Poskytovatelé dat

  • Musí být public funkcí.
  • Should be static functions. (See T332865.)
  • Název poskytovatele dat by měl být uveden v lowerCamelCase a začínat slovem provide. Např. provideHtml5InputTypes.
  • Neměli byste vytvářet instanci služeb MediaWiki, protože poskytovatelé dat jsou voláni přes setUpBeforeClass. Například místo Title::newFromText použijte new TitleValue, abyste se vyhnuli vytvoření TitleParser.

Sdělení

Výstup testu by měl sdělit závěr. Výstupní formát --testdox je dobrý způsob, jak si toto sdělení prohlédnout: Provedení testovací sady se zobrazí jako sada prohlášení o testovacích třídách spolu s tím, zda prošly nebo ne. Příkazy (pokud nejsou přizpůsobeny) jsou názvy testovacích metod s opravenými velkými písmeny a mezerami.

Anotace @testdox lze použít k přizpůsobení zobrazené zprávy. V současné době se v kódové základně MediaWiki nepoužívá.

Další informace viz "Další použití pro testy".

Počet tvrzení

Pouze jedno tvrzení na test, pokud k tomu není dobrý důvod (možná bude nutné seskupit cenné testy).

Selhání

Obvykle by testovací kód neměl dělat die("Error"), ale použijte metodu selhání phpunit:

$this->fail( 'A useful error message' )

To by se projevilo jako selhání v souhrnu testování, než aby se celá sada testů zhroutila.

Specifické pro MediaWiki

Testy seskupení

PHPUnit umožňuje zařadit testy do libovolných skupin. Skupiny testů lze vybrat pro provedení nebo je vyloučit z provádění při spuštění testovací sady (viz dokumentace @group annotation, The Command-Line Test Runner a XML Configuration File v dokumentaci PHPUnit příručce pro další podrobnosti.)

Chcete-li přidat test (nebo třídu) do skupiny, použijte anotaci @group v docblock před kódem. Například:

/**
 * @group Broken
 * @group Internationalization
 */
class HttpTest extends MediaWikiTestCase {
    ...

V testech jednotek MediaWiki se v současnosti používá několik funkčních skupin:

  • API – testy, které provádějí MediaWiki API.
  • Broken – rozbité testy zařaďte do skupiny Broken (rozbité). testy z této skupiny nebudou spuštěny (jak je nakonfigurováno v tests/phpunit/suite.xml).
  • Database – testy, které vyžadují připojení k databázi, by měly být umístěny do skupiny Database.
To způsobí, že se dočasné tabulky překryjí přes skutečnou wiki databázi, takže testovací případy mohou provádět databázové operace bez změny skutečné wiki.
  • Destructive – testy, které mění nebo ničí data, by měly být zařazeny do skupiny Destructive (destruktivní).
  • Search – testy, které používají vestavěné vyhledávání MediaWiki, se vkládají do skupiny Search (vyhledávání).
  • SeleniumFramework – testy, které vyžadují instalaci SeleniumFramework , by měly být zařazeny do skupiny SeleniumFramework.
  • Stub – testovací útržky vložte do skupiny Stub (útržky). Testy z této skupiny nebudou spuštěny (jak je nakonfigurováno v tests/phpunit/suite.xml).
  • Sqlite – testy, které používají SQLite, by měly být vloženy do skupiny Sqlite.
  • Standalone – velmi pomalé testy, které by neměly být spouštěny v gate, aby pomohly udržet rychlé testy pro jiná gated rozšíření.
  • Upload – testy, které nahrávají soubory, by měly být umístěny do skupiny Upload.
  • Utility – momentálně nevyužito žádným testem. Testy v této skupině nebudou spuštěny (jak je nakonfigurováno v tests/phpunit/suite.xml).

Kromě toho mohou být testy seskupeny na základě rozhodnutí vývojového týmu:

  • Fundraising (získávání finančních prostředků)
  • EditorEngagement (zapojení editora)
  • Internationalization (internacionalizace)
  • atd.

Chcete-li otestovat pouze určitou skupinu, použijte příznak --group v příkazového řádku:

composer phpunit:entrypoint -- --group Search

nebo pokud používáte Makefile v core/tests/phpunit:

make FLAGS="--group Search" target

kde cíl může být phpunit, safe atd.

Pokrytí

PHPUnit dokumentace obsahuje kapitolu o pokrytí. Dvakrát denně se generuje přehled pokrytí $url pro jádro MediaWiki. Protože by měla platit možnost forceCoversAnnotation, test by měl být označen @covers annotations, aby se vyjádřilo, které části kódu test skutečně kontroluje (na rozdíl od kódu, který je právě spuštěn, ale jehož výsledky nejsou nikdy testováno pomocí tvrzení).

Všimněte si, že @covers vyžaduje plně kvalifikované názvy tříd (na rozdíl od anotací Doxygen , jako je @param).

Třídy

Budete chtít rozšířit jednu z testovacích tříd MediaWiki.

class HttpTest extends MediaWikiTestCase {
    ...

Níže jsou uvedeny některé běžné testovací třídy MediaWiki, které lze rozšířit. Odrážkový seznam na nižší úrovni prodlužuje jeho rodiče.

  • TestCase – testovací třída PHPUnit
    • MediaWikiUnitTestCase – pro jednotkové testy metod bez závislostí nebo metod, jejichž závislosti jsou zcela zesměšňovány. Ty by měly být umístěny ve své vlastní podsložce nazvané /unit/, aby phpunit:unit fungovalo správně.
      • HookRunnerTestBase – testuje, že všechny argumenty předané do třídy HookRunner jsou předány do HookContainer.
    • MediaWikiIntegrationTestCase – pomáhá s testováním tříd, které přistupují ke globálním proměnným, metodám, službám nebo backendu úložiště. Zabraňuje odesílání skutečných e-mailů. Ty by měly být umístěny ve své vlastní podsložce nazvané /integration/, aby phpunit:integration fungovalo správně. Můžete bezpečně otestovat databázi SQL, pokud k testům přidáte @group Database. Změny databázových dat se resetují na začátku každé testovací metody.
      • MediaWikiLangTestCase – provede nějaké nastavení jazyka, například $this->setUserLang( 'en' ); a $this->setContentLang( 'en' );, a provede $services->getMessageCache()->disable()
        • ApiTestCase –- má nějaké další metody pro testování Action API , jako doApiRequest(), doApiRequestWithToken(), buildFauxRequest() atd.
      • MaintenanceBaseTestCase – pro testování tříd údržby MediaWiki.
      • SpecialPageTestBase – pro testování speciálních stránek MediaWiki.

Databáze

Při testování kódu závislého na databázi byste měli svůj testovací případ umístit do skupiny Database (viz nahoře). To říká MediaWikiTestCase, aby nastavilo připojení k databázi DB_PRIMARY, které můžete použít v $this->db. Normálně používá samostatnou dočasnou databázi s určitými omezenými údaji předem vyplněnými addCoreDBData, včetně uživatele 'UTSysop' a názvu 'UTPage'. (Dočasná databáze je vytvořena s CREATE TEMPORARY TABLE a tabulky mají předponu unittest_.) Můžete vynutit, aby PHPUnit nepoužíval dočasné tabulky (například pokud chcete provést krokové ladění a podívat se do databáze pomocí prohlížeče databází), nastavením proměnné .env PHPUNIT_USE_NORMAL_TABLES=1. Testovací případ může do databáze přidat další data přepsáním addDBData (což ve výchozím nastavení nic nedělá).

Upozorňujeme však, že z vaší skutečné databáze se zkopíruje pouze schéma tabulky, nikoli existující data v této tabulce.

Aktuální obsah databáze můžete přímo otestovat pomocí assertSelect().

$this->assertSelect(
	'test', // Tabulka
	[ 'first_name', 'last_name', 'street' ], // Vybraná pole
	[], // Podmínky
	[ [ 'Jane', 'Doe', 'Broadway' ] ] // Očekávané hodnoty
);

Zde je několik příkladů z rozšíření, na která se můžete podívat pro referenci:

Testy, které nejsou ve skupině Database, stále běží proti dočasné klonované databázi (i když ignorují $this->db a místo toho přímo používají např. wfGetDB()). Tato databáze je však nastavena pouze jednou pro celý testovací běh a mezi testovacími běhy se neresetuje. Testy by se pokud možno neměly spoléhat na tento bezpečnostní prvek.

Údržbářské skripty

Testovací případy pro skripty údržby by měly dědit z MediaWiki\Tests\Maintenance\MaintenanceBaseTestCase, aby zvládly různé výstupní kanály používané údržbovými skripty.

Metoda základního testovacího případu setUp() za vás vytvoří instanci vašeho objektu Maintenance, pokud zadáte třídu, kterou chcete sestavit, poskytnutím povinných getMaintenanceClass() ve vaší podtřídě:

    public function getMaintenanceClass() {
        return PurgeScoreCache::class;
    }

V nepravděpodobném případě, že chcete udělat něco speciálního pro vytvoření instance testované třídy, můžete přepsat metodu createMaintenance(), ale doufejme, že to není potřeba.

Ve výchozím nastavení bude výstup skriptu údržby potlačen a ignorován. Pokud chcete otestovat výstup (to je dobrý nápad), použijte kód jako:

use MediaWiki\Tests\Maintenance\MaintenanceBaseTestCase;

class PurgeScoreCacheTest extends MaintenanceBaseTestCase {
    public function testNotAThing() {
        $this->maintenance->loadWithArgv( [ '--model', 'not_a_thing' ] );
        $this->maintenance->execute();

        $this->expectOutputRegex( '/skipping \'not_a_thing\' model/' );
    }
}