$30 off During Our Annual Pro Sale. View Details »

Testování prakticky

Testování prakticky

Jak začít testovat, jak nad tím přemýšlet a na co si dát pozor. Včetně přehledu užitečných nástrojů a jak na co který použít.

Filip Procházka

December 21, 2015
Tweet

More Decks by Filip Procházka

Other Decks in Technology

Transcript

  1. Co si povíme - troška teorie - nette/tester - mockery/mockery

    - testování proti síti - integrační testy - codeception/codeception - selenium
  2. Troška teorie - co testovat - že to funguje -

    že to nefunguje (když nemá) - jak testovat - unit testy - integrační testy - akceptační testy - selenium (high level akceptační testy) - Test Driven Development (red, green, refactor) - Behaviour Driven Development
  3. tester: Tester\Assert $obj = new Math(); Assert::exception(function() use ($obj) {

    $obj->divide(2, 0); }, \InvalidArgumentException::class, 'Cannot divide by zero');
  4. tester: TestCase class SomeTest extends Tester\TestCase { function setUp() {

    } function tearDown() { } function testOne() { Assert::same(......); } } (new SomeTest)->run();
  5. tester: TestCase @dataProvider public function dataLoop() { return [ [1,

    2, 3], [4, 5, 6], [7, 8, 9], ]; } /** @dataProvider dataLoop */ public function testLoop($a, $b, $c) { // ... }
  6. tester: TestCase @dataProvider public function dataLoop() { yield [1, 2,

    3], yield [4, 5, 6], yield [7, 8, 9], } /** @dataProvider dataLoop */ public function testLoop($a, $b, $c) { // ... }
  7. tester: metadata <?php /** * @testCase */ require __DIR__ .

    '/../bootstrap.php'; class SomeTest extends Tester\TestCase {
  8. _____ ___ ___ _____ ___ ___ |_ _/ __)( __/_

    _/ __)| _ ) |_| \___ /___) |_| \___ |_|_\ v1.6.1 PHP 7.0.0 | 'php-cgi' -n | 10 threads ........s................F......... -- FAILED: tests/greeting.phpt Failed: 'Hello John' should be ... 'Hi John' in src/Framework/Assert.php(370) in src/Framework/Assert.php(52) Tester\Assert::fail() in tests/greeting.phpt(6) Tester\Assert::same() FAILURES! (35 tests, 1 failures, 1 skipped, 1.7 seconds)
  9. tester: a další... - data providery & multipliery (meta) -

    skipování testů - DomQuery na testování HTML & XML - file mock - zámky (aby se nemlátily paralelní testy) - watch (automatické spouštění testů při změně kódu) - generování code coverage - podpora HHVM
  10. mockery: testovaná třída class Temperature { function __construct(WeatherApi $weather); function

    average() { $total = 0; for ($i = 0; $i < 3 ;$i++) { $total += $this->weather->readTemp(); } return $total / 3; }
  11. mockery: test v Testeru class TemperatureTest extends Tester\TestCase { function

    testGetsAverageTemperature() { $service = \Mockery::mock(WeatherApi::class); $service->shouldReceive('readTemp') ->times(3)->andReturn(10, 12, 14); $temperature = new Temperature($service); Assert::same(12, $temperature->average()); }
  12. mockery: test v Testeru // ... function tearDown() { \Mockery::close();

    \Tester\Environment::$checkAssertions = FALSE; }
  13. Testování proti síti - je to pomalé - síť nemusí

    fungovat - formát/protocol se může změnit - služba může mít výpadky
  14. Testování proti síti try { $result = $paymentProcessor->process($order); } catch

    (Orders\CardPaymentException $e) { if (preg_match('~SoapClient.*? Connection (refused|timed out)~', $e->getMessage())) { Tester\Environment::skip($msg); } throw $e; }
  15. Testování proti síti - knihovna používá psr/http-message - výchozí je

    guzzle klient - testy používají fake klienta - api se reálně zavolá pouze poprvé - výsledek se uloží - další běhy načítají odpovědi z disku - když soubor smažu, request se provede Ukázka v testech Kdyby/CsobPaymentGateway na githubu
  16. Integrační testy abstract class IntegrationTestCase extends TestCase { function getService($type);

    function getContainer() {} function createContainer() {} abstract class DbTestCase extends IntegrationTestCase { function createContainer() {} function setupDatabase(Connection $db);
  17. Integrační testy: databáze class CartTest extends IntegrationTestCase { function testAdd()

    { $cart = $this->getService(OrderCart::class); $cart->addItem(10); // položka s id 10 $itemsInDb = $this->getService(Connection::class) ->query("SELECT id FROM order_items WHERE order = 1"); Assert::same([10], $itemsInDb); }
  18. Integrační testy: presentery abstract class PresenterTestCase extends DbTestCase { function

    usePresenter($name); function runPresenterAction($action, ...); function makeAjax(); function fakeCsrfProtection(); function fakeRedirectFromWebpay($action, ...); function fakeRedirectFromCsob($action, ...);
  19. Integrační testy: presentery class HomepagePresenterTest extends PresenterTestCase { function setUp()

    { parent::setUp(); $this->usePresenter('Front:Homepage'); } function testRenderDefault() { $response = $this->runPresenterAction('default'); Assert::type(TextResponse::class, $response); }
  20. Integrační testy: komponenty abstract class ComponentTestCase extends DbTestCase { function

    attachToPresenter($component, ...); function loadState($params, ...); function runSignal($signal, $params, ...); function makeAjax(); function assertRedirect($url); function assertSnippets($snippets);
  21. Integrační testy: komponenty class CartControlTest extends ComponentTestCase { function testCopyItem()

    { $basket = $this->getService(Cart::class); $item = $basket->addItem(719693); $control = $this->getService(ICartControlFactory::class)->create(); $this->attachToPresenter($control); $this->runSignal('copyItem', ['item' => $item->getId()]); // ...
  22. codeception: simple test $I = new AcceptanceTester($scenario); $I->wantTo('see note can

    be successfully created'); $I->testLogin(); $I->amOnPage('/notes/create'); $I->fillField('Name', 'Example note'); $I->fillField('Text', 'Lorem ipsum'); $I->selectOption('Pad', 1); $I->click('Save'); $I->see('Example note');
  23. codeception: cest test class SigninCest { function loginWithPassword(Homepage $homepage) {

    $homepage->open(); $homepage->login('[email protected]', '12345678'); $homepage->seeThatUserIsLoggedIn(); }
  24. codeception: page object class Homepage extends FrontBasePage { function open()

    { $I = $this->tester; $I->amOnUrl($this->app->url(':Front:Homepage:')); }
  25. codeception: page object abstract class FrontBasePage { function __construct(WebGuy $tester,

    NetteApp $app); function login($email, $password) { $form = new LoginForm($this->tester); $form->openLoginDialog(); $form->login($email, $password); }
  26. codeception: page component class LoginForm { function __construct(WebGuy $tester); function

    login($email, $password) { $I = $this->tester; $I->fillField(['name' => 'email'], $email); $I->fillField(['name' => 'password'], $password); $I->click(['css' => '.form-login form .login-btn button[type=submit]']); }
  27. codeception: nette app integration class NetteApp { function __construct(NetteSetup $netteSetup);

    function url($fqa, $args = []); function getService($type); function getContainer();