Behat: auto-testez votre application

Behat: auto-testez votre application

Les tests unitaires sont excellents, mais seuls, ils ne vous disent pas si votre application se comporte comme prévu. Un utilisateur peut-il accéder aux enregistrements de quelqu'un d'autre? Vos traductions fonctionnent-elles correctement en fonction des headers des requêtes? Apprenez comment écrire des tests Behat pertinents, comment configurer le tout et comment le backend et le frontend peuvent être développés en parallèle.

B3b2139e4f2c0eca4efe2379fcebc1c5?s=128

Anna Filina
PRO

April 05, 2018
Tweet

Transcript

  1. @afilina Behat: Why and How? PHP Québec | April 5,

    2018 | Montreal
  2. • Project rescue expert. • Dev, mentor, public speaker. •

    Consultant at Zenika. Anna Filina
  3. Behavior: does it do what
 the user wants?

  4. class CartTest extends TestCase { public function testGetTax() { $mock

    = $this->getMock('Cart',['getTaxRate']); $mock->expects($this->once()) ->method('getTaxRate') ->will($this->returnValue(0.15)); $this->assertEquals(15, $mock->getTax(100)); } }
  5. Feature: Cart Items In order to let customers purchase products

    As a developer I need to add items to the shopping cart Background: Given the fixtures file "cart_items.yml" is loaded Scenario: List own cart items Given I send a "GET" request to "/api/users/1/cart" Then the response status code should be 200 And the JSON node "data" should have 2 elements
  6. $ vendor/bin/behat Feature: Cart Items In order to let customers

    purchase products As a developer I need to add items to the shopping cart Background: Given the fixtures file "cart_items.yml" is loaded
 Scenario: List own cart items Given I send a "GET" request to "/api/users/1/cart" Then the response status code should be 200 And the JSON node "data" should have 2 elements 1 scenario (1 passed) 4 steps (4 passed) 0m0.31s (29.05Mb)
  7. Feature: Cart Items In order to let customers purchase products

    As a developer I need to add items to the shopping cart Background: Given the fixtures file "cart_items.yml" is loaded Scenario: List own cart items Given I send a "GET" request to "/api/users/1/cart" Then the response status code should be 200 And the JSON should be equal to file "users_1_cart.json"
  8. /api/users/1/cart { "data": [ { "id": 2, "name": "Product 2",

    "price": 10, "quantity": 2 }, { "id": 1, "name": "Product 1", "price": 10, "quantity": 2 } ] } Backend dev Frontend dev
  9. --- Expected +++ Actual @@ @@ "data": [ { "id":

    2, - "name": "Product 2", + "name": "WRONG DATA", "price": 10, "quantity": 2 },
  10. More than just
 JSON matching.

  11. Scenario: Unauthenticated list Given I send a "GET" request to

    "/api/users/1/cart" Then the response status code should be 401 Scenario: List own cart items Given I add "Authorization" header equal to "Bearer TOKEN_1" And I send a "GET" request to "/api/users/1/cart" Then the response status code should be 200
  12. Scenario: List another user's cart items Given I add "Authorization"

    header equal to "Bearer TOKEN_1" And I send a "GET" request to "/api/users/2/cart" Then the response status code should be 403
  13. Bug in Mobile App Items GET /api/users/1/cart Authorization: Bearer TOKEN_1

    Cart
  14. Background: Given the fixtures file "expired_token.yml" is loaded Scenario: Expired

    token Given I add "Authorization" header equal to "Bearer TOKEN_1" And I send a "GET" request to "/api/users/1/cart" Then the response status code should be 401
  15. Scenario: List cart items in French Given I add "Accept-Language"

    header equal to "fr-CA,en;q=0.9"
 And I send a "GET" request to "/api/users/1/cart" Then the response status code should be 200 And the JSON should be equal to file "users_1_cart_fr.json"
  16. Scenario: List own cart items Given I add "Authorization" header

    equal to "Bearer TOKEN_1" And I send a "GET" request to "/api/users/1/cart" Then the response status code should be 200 And max 3 queries were executes And query time is under 0.05 seconds
  17. I need my API to go faster. How much faster?

    It takes 3s.
 Bring it under 0.5 Got it.
  18. Custom Steps

  19. Given I add "Authorization" header equal to "Bearer TOKEN_1"
 Given

    I send a "GET" request to "/api/users/1/cart"
 Then the response status code should be 200
  20. Given the fixtures file "expired_token.yml" is loaded 
 Then max

    3 queries were executes
 Then query time is under 0.05 seconds
  21. class JsonContext extends \Behatch\Context\JsonContext { /** * @Then the JSON

    should be equal to file :filename */ public function theJsonShouldBeEqualToFile(string $filename):void { $actualJson = json_encode( $this->getJson()->getContent(), JSON_PRETTY_PRINT ); $expectedJson = file_get_contents( __DIR__ . '/../../json/'.$filename ); PHPUnit\Framework\Assert::assertJsonStringEqualsJsonString( $expectedJson, $actualJson ); } }
  22. /** * @Given the fixtures file :filename is loaded */

    public function loadYamlFixtures(string $filename):void { $loader = new \Nelmio\Alice\Loader\NativeLoader(); $objectSet = $loader->loadFile(__DIR__.'/../../fixtures/'.$filename); foreach ($objectSet->getObjects() as $object) { $className = get_class($object); $this->em->persist($object); } $this->em->flush(); }
  23. More than just
 REST APIs.

  24. None
  25. Feature: Product search Background: Given the fixtures file "products.yml" is

    loaded Scenario: Search existing products Given I am on "/products/Elder+Scrolls" Then the response status code should be 200 And I should see 5 "div.product" elements
  26. Feature: Product search Background: Given the fixtures file "products.yml" is

    loaded Scenario: Search existing products Given I am on "/products/Elder+Scrolls" Then the response status code should be 200 And I should see 5 "div.product" elements 1 scenario (1 passed) 4 steps (4 passed) 0m0.28s (29.15Mb)
  27. Goutte Web Server Behat Behat
 Goutte Driver Started before tests

  28. But my JavaScript!

  29. Selenium Browser Behat Behat
 Selenium Driver Selenium Server Started before

    tests Web Server Browser's Driver
  30. composer require --dev \ behat/mink-selenium2-driver \
 se/selenium-server-standalone
 
 
 npm

    install chromedriver

  31. None
  32. @javascript Scenario: Filter products Given I am on "/products" When

    I fill in "search" with "path" Then I should see 1 "div.product.visible" element No :visible pseudoclass
  33. Feature: Product search Background: Given the fixtures file "products.yml" is

    loaded @javascript Scenario: Filter products Given I am on "/products" When I fill in "search" with "path" Then I should see 1 "div.product.visible" element 1 scenario (1 passed) 4 steps (4 passed) 0m2.27s (20.53Mb)
  34. Browser tests are
 hardest to maintain.

  35. • Limit number of browser tests. • Test the JS

    using JS tooling. • Focus on critical path (checkout). Tips
  36. Behat can do
 unit tests, but...

  37. • Does the app do what the user wants? •

    Useful for "design by contract". • Security, performance, etc. • API, HTML & browser. Takeaways
  38. @afilina | afilina.com | youtube.com/c/AnnaFilina