Jak testovat, že něco nehodí výjimku?
- amik
- Člen | 118
Ahoj,
jak správně a čistě otestovat, že kód nevyhodí chybu/výjimku?
Hodilo by se něco jako Assert::noError(callback)
, bohužel nic
takového v testeru nevidím.
Use case: mám konstruktor přijímající nějakou strukturu parametrů, u některých povoluji více typů a typovost kontroluji až uvnitř konstruktoru a v případě chybného parametru házím InvalidArgumentException.
Příklad: mám třídu pro testování presenterů, kterou můžu nakrmit buď objektem presenteru, nebo stringem – jménem třídy presenteru, nebo callbackem (factory metodou):
public function __construct($presenterCreator)
{
if (!($presenterCreator instanceof Presenter || is_string($presenterCreator)
|| is_callable($presenterCreator))) {
throw new \Nette\InvalidArgumentException(
"Presenter must be Presenter object, class name or callback factory");
}
...
}
... // třída TestCase:
public function testConstructor() {
new PresenterTester('\App\HomepagePresenter');
new PresenterTester(new HomepagePresenter);
new PresenterTester(function() { return new HomepagePresenter; });
Assert::true(TRUE); //aby tester neřval, že se nespustil žádný assert
}
Chci test, který mi ověří, že mám parametry dobře, a tedy když předám do konstruktoru povolený typ, tak to projde (třeba kdybych v definici udělal překlep v názvu třídy Presenter, test to zjistí). Udělat to nějak není problém, ale jak na to čistě? mě napadá:
- neřešit, jak je příklad výše – pokud vyletí chyba nebo výjimka, test stejně failne, ale není to moc elegantní, testy by měly v tom, co testují, failovat na assertech a neházet neošetřené výjimky.
- udělat něco jako
Assert::type('PresenterTester', new PresenterTester(...))
– to se mi nelíbí, je to hromada zbytečného boilerplatu a testovat, že konstruktor vrací správný typ, je divné. - hodit pull request do Testeru, kde přidám podporu Assert::noError(callback, description), který failne, pokud zabalený kód hodí výjimku nebo běhovou chybu, ale failne čistě assertem, nevyletí výjimka/chyba ven. Zároveň je to podle mě sémanticky nejlépe čitelné řešení. Rád uslyším názory, než se do toho pustím.
- David Matějka
- Moderator | 6445
Tak noError assert by mel jeste vice boilerplate kodu, ale je zas pravda, ze
by to bylo vic expresivni. A plus by slo asi pridat nejakou
@noError
anotaci podobne jako je anotace @throws
Vetsinou to resim jako ty, tedy vykonat kod a na
konci assert::true
@looky to je opak toho, co chce :)
- David Matějka
- Moderator | 6445
@Tharos to bych si musel vzdycky slozite vzpominat, ze je to v Environment :)
A lepsi nez noError anotace bude treba @noAssert
- Milo
- Nette Core | 1283
Možná by něco ve stylu noError()
přišlo vhod, hlavně
z pohledu čitelnosti.
Tohle je navíc trochu specifické, protože testuješ konstruktor. U běžné metody bys testoval návratovou hodnotu a nečekaná chyba/vyjímka by ukončila test, tak jak se Ti to teď nepozdává.
Ještě mě napadá:
Assert::error(function () {
...
...
...
}, []);
- llook
- Člen | 407
Obojí je IMHO WTF. Já používám
Environment::$checkAssertions = FALSE
, protože to tak používáme
ve firmě. Assert::true(TRUE)
by mi neprošlo přes review
(vyzkoušeno). Ale pokud vím, tak v PHPUnit se běžně používá
assertTrue(true)
, je to sice podivný, ale celkem zažitý zápis.
Jedno z toho si vyber a to používej.
Kdo pamatujete SimpleTest (PHP4), tak ten k tomu míval metodu
pass()
, která inkrementovala počet splněných asercí. To by se
mi v Testeru taky líbilo…
- amik
- Člen | 118
Jasně – jeden důvod je ten, že chci čistě zapsat, že test nepotřebuje Assert. na to by Environment::$checkAssertions stačilo.
Druhý důvod je expresivita (můj návrh je právě metoda, kterou explicitně řeknu, že něco nemá hodit výjimku, a ne jen že prostě pustím test a tím implicitně čekám, že nespadne).
Třetí důvod je taky ten, že když test selže na Assertu, hodí to hezkou custom message (třeba ‚Constructor of PresenterTester should accept instance of Presenter, but \SomeException was thrown‘).
Použili byste sami takovou věc, nebo zůstali u starého checkAssertions?
- Michal Vyšinský
- Člen | 608
Ahoj, osobně testuji „nevyhození“ vyjímky tak, že obalím testované
volání try-catch blokem a v catch zavolám
Assert::fail('Exception was thrown.')
.