Behat: Why and How?

Behat: Why and How?

Unit tests are great, but on their own, they don't tell you whether your application behaves as expected. Can a user access someone else's records? Do your translations work correctly based on request headers? Learn how to write relevant Behat tests, how to set everything up and how a backend and a frontend can be developed in parallel.

B3b2139e4f2c0eca4efe2379fcebc1c5?s=128

Anna Filina

March 07, 2018
Tweet

Transcript

  1. 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)); } }
  2. 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
  3. 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)
  4. 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"
  5. 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
  6. 9.

    --- Expected +++ Actual @@ @@ "data": [ { "id":

    2, - "name": "Product 2", + "name": "WRONG DATA", "price": 10, "quantity": 2 },
  7. 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
  8. 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
  9. 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
  10. 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"
  11. 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
  12. 17.

    I need my API to go faster. How much faster?

    It takes 3s.
 Bring it under 0.5 Got it.
  13. 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
  14. 20.

    Given the fixtures file "expired_token.yml" is loaded 
 Then max

    3 queries were executes
 Then query time is under 0.05 seconds
  15. 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 ); } }
  16. 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(); }
  17. 24.
  18. 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
  19. 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)
  20. 31.
  21. 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
  22. 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)
  23. 35.

    • Limit number of browser tests. • Test the JS

    using JS tooling. • Focus on critical path (checkout). Tips
  24. 37.

    • Does the app do what the user wants? •

    Useful for "design by contract". • Security, performance, etc. • API, HTML & browser. Takeaways