Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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. Testování prakticky @ProchazkaFilip

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

    - testování proti síti - integrační testy - codeception/codeception - selenium
  3. 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
  4. None
  5. nette/tester

  6. tester: motivace $obj = new Math; $result = $obj->add(1, 2);

    var_dump($result);
  7. tester: motivace $obj = new Math; $result = $obj->add(1, 2);

    Assert::same(3, $result);
  8. tester: tests/bootstrap.php require __DIR__ . '/../vendor/autoload.php'; Tester\Environment::setup(); date_default_timezone_set('Europe/Prague');

  9. tester: tests/math/add.phpt require __DIR__ . '/../bootstrap.php'; $obj = new Math;

    $result = $obj->add(1, 2); Assert::same(3, $result);
  10. tester: Tester\Assert Assert::same($expected, $actual); Assert::equal($expected, $actual); Assert::contains($needle, $actual); Assert::true($value); Assert::false($value);

    Assert::type($type, $value); // ...
  11. tester: Tester\Assert $obj = new Math(); Assert::exception(function() use ($obj) {

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

    } function tearDown() { } function testOne() { Assert::same(......); } } (new SomeTest)->run();
  13. tester: TestCase @throws /** * @throws InvalidArgumentException */ public function

    testOne() { // ... }
  14. tester: TestCase @dataProvider public function dataLoop() { return [ [1,

    2, 3], [4, 5, 6], [7, 8, 9], ]; } /** @dataProvider dataLoop */ public function testLoop($a, $b, $c) { // ... }
  15. 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) { // ... }
  16. tester: metadata <?php /** * @testCase */ require __DIR__ .

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

    _/ __)| _ ) |_| \___ /___) |_| \___ |_|_\ 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)
  18. 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
  19. mockery/mockery

  20. 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; }
  21. 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()); }
  22. mockery: test v Testeru // ... function tearDown() { \Mockery::close();

    \Tester\Environment::$checkAssertions = FALSE; }
  23. Testování proti síti

  24. 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
  25. 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; }
  26. 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
  27. Testování proti síti - můžu (alespoň částečně) vyvíjet offline -

    testy náhodně nepadají - testy jsou rychlé
  28. Integrační testy

  29. 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);
  30. 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); }
  31. 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, ...);
  32. 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); }
  33. 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);
  34. 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()]); // ...
  35. Integrační testy: komponenty // ... Assert::same(2, $item->getCount()); $this->assertSnippets([ 'snippet-user-panel', 'snippet-review',

    'snippet-control-cart' => '%A%Milka Tender oříšková%A%' ]);
  36. codeception/codeception

  37. None
  38. codeception: tests/acceptance.suite.yml class_name: AcceptanceTester modules: enabled: - PhpBrowser: url: 'http://127.0.0.1:8000'

    - \Helper\Acceptance
  39. 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');
  40. selenium

  41. codeception: tests/acceptance.suite.yml class_name: WebGuy modules: enabled: - Codeception\Module\WebDriver - SeleniumTests\Support\Helper\NetteSetup

    config: Codeception\Module\WebDriver: url: 'http://www.rohlik.l/' browser: 'firefox'
  42. codeception: cest test class SigninCest { function loginWithPassword(Homepage $homepage) {

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

    { $I = $this->tester; $I->amOnUrl($this->app->url(':Front:Homepage:')); }
  44. 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); }
  45. 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]']); }
  46. codeception: nette app integration class NetteApp { function __construct(NetteSetup $netteSetup);

    function url($fqa, $args = []); function getService($type); function getContainer();
  47. Díky za pozornost! Dotazy? @ProchazkaFilip