Příručka:Testování PHP kódu/Píšeme testy jednotky
Testování PHPUnit
Obsah
- Provádění testů
- Vygenerování pokrytí kódu
- Writing testable PHP code
- Psaní testů
- Souvislá integrace
- Pochopení selhání sestavení
- Dodatek
(jak pomoci, zdroje..)
Nástroje
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 vHtmlTest::testExpandAttributes
.
Poskytovatelé dat
- Musí být
public
funkcí. - 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ístoTitle::newFromText
použijtenew 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 PHPUnitMediaWikiUnitTestCase
– 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/
, abyphpunit: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/
, abyphpunit: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 , jakodoApiRequest()
,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:
- PruneOldBounceRecordsTest – od Extension:BounceHandler
- PageAssessmentsDAOTest – od Extension:PageAssessments
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/' );
}
}