Symfony + Docker + Behat + BrowserStack: testing your app like a boss

Introduction

My colleague Damien Gilbrin has a lot of experience about Behat. He just partecipate to a MeetUp where he explained how to properly use Behat also integrating Selenium and BrowserStack.

Here the presentation in french language:

Now i will show you the practical example that Damien publish on github

Stack

  • Docker
  • Symfony 4
  • Behat
  • Selenium
  • BroswerStack

Installation

  • You must have Docker installed
  • Retrieve the sources repository (git clone)
  • Copy the .env.dist file to .env (and modify it if you use BrowserStack)
  • Launch the Docker container via the docker-compose up command
  • Install the vendors:
    • Run the command docker exec -it behatmeetupzendfr_php_1 bash to connect to the PHP container
    • Execute this command to install the vendors: composer install

Once the containers are running, go to the address: http://localhost:8080

Important files

The example is a simple cart, where you can add a product, remove it or see the list of products included in your cart. The entry point is the DefaultController:

  • src/Controller/DefaultController.php

A service takes care of performing operations in memory:

  • src/Service/CartServiceSession.php

The scenarios of Behat are here:

  • tests/Behaviour/Features

The Context are here:

  • tests/Behaviour/Bootstrap/Context

 

A quick tour inside code

In this article I do not want to present Behat, for this I highly recommend reading the documentation. It is however interesting to see how easily you can test your application.

For this reason we are going to write our first Behat scenario:

//tests/Behaviour/Features/Interface/Cart.feature
@cart @javascript
Feature: Cart interface Test

  @cart-add-1
  Scenario: Add 1 product to cart
    Given I am on home page
    When I add "Article A" to my cart
    And I see my cart
    Then the cart contain 1 "Article A" product
    And I have 1 article on cart
    And the total amount is 10.00 Euro

Each sentence of the scenario is linked (by annotations) to a method defined in the context. So the sentence I am on home page, it’s translated by the method iAmOnHomePage of tests/Behaviour/Bootstrap/Context/CartContext.php.

   /**
     * @Given /^I am on home page$/
     */
    public function iAmOnHomePage()
    {
        $this->visit('/');
    }

and in the same way the sentence I add “Article A” to my cart it’s translated by the method iAddToMyCart

   /**
     * @When /^I add (\d+ )?"([^"]*)" to my cart$/
     * @param string $productName
     * @throws Exception
     */
    public function iAddToMyCart(string $quantity, string $productName)
    {
        //Use default session, you can update to add "MyBrowserStackChrome" for specific test on navigator
        $session = $this->getMink()->getSession();

        //Find all article
        $articleElementList = $session->getPage()->findAll('css', '.article');
        foreach ($articleElementList as $articleElement) {
            //Is the expected article ?
            /** @var $articleElement ElementInterface */
            if ($articleElement->find('css', '.title')->getText() != $productName) {
                continue;
            }

            //Iterate for count click (add 1 time, 2...)
            $countIterate = trim($quantity) === '' ? 1 : (int) trim($quantity);
            for ($count = 0; $count < $countIterate; $count ++ ) {
                //Click on button
                $button = $articleElement->find('css', 'button');
                $button->click();

                //Wait button is not disabled (ajax request)
                $session->wait(
                    20000,
                    sprintf(
                        '!document.getElementById(\'%s\').disabled',
                        $button->getAttribute('id')
                    )
                );
            }
            //It's ok !
            return;
        }

        //Article not found
        throw new \Exception(sprintf('Article %s not found', $productName));
    }

You can also use PHPUnit asserts. This is just an example to show how it works.

It should be noted that Behat by default uses Mink. Mink is a browser emulator abstraction layer. It hides emulator differences behind a single, consistent API. So you can use Mink + Selenium to follow in real time yours tests step by step.

For this reason the project use Docker Selenium, so in docker-compose.yml we will have:

     chrome:
        image: elgalu/selenium
        depends_on:
          - hub
        volumes:
          - /dev/shm:/dev/shm
        privileged: true
        environment:
          - NOVNC=true
          - SELENIUM_HUB_HOST=hub
          - SELENIUM_HUB_PORT=4444
          - SELENIUM_NODE_HOST={{CONTAINER_IP}}
          - SCREEN_WIDTH=1300
          - SCREEN_HEIGHT=999
          - VIDEO=false
          - GRID=false
          - CHROME=true
          - FIREFOX=false

 

Launch Behat tests

Now you can run tests:

php vendor/bin/behat -vvv --no-interaction --tags=XXXXX

You can add –tags = TagName with the name of the tag using “@” symbol in the Feature file.

And with Selenium it is possible to see in real time the execution of the script at this address: http://127.0.0.1:6081

 

Browser Stack

The last piece of the puzzle is BrowserStack. If you don’t want use Selenium you can use BrowserStack.

BrowserStack is a cloud web and mobile testing platform that enables developers to test their websites and mobile applications across on-demand browsersoperating systems and real mobile devices, without requiring users to install or maintain an internal lab of virtual machines, devices or emulators.

So if you want use it in the project you can add to docker-compose.yml

     browserstacklocal:
        build:
            context: ./docker/browserstacklocal
        env_file:
          - .env
        depends_on:
          - nginx


And you must update the Dockerfile:

FROM debian:stable-slim

ENV BROWSERSTACK_LOCAL_KEY=

COPY ./entrypoint.sh /
RUN ["chmod", "+x", "/entrypoint.sh"]

RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y wget unzip

RUN wget https://www.browserstack.com/browserstack-local/BrowserStackLocal-linux-x64.zip && \
    unzip BrowserStackLocal-linux-x64.zip && \
    rm BrowserStackLocal-linux-x64.zip && \
    chmod +x BrowserStackLocal && \
    mv BrowserStackLocal /usr/local/bin

ENTRYPOINT ["/entrypoint.sh"]

And in .env you must add yours BrowserStack credentials.

Conclusions

The advantages of using Behat are manifold and are well explained in this article. And it’s very easy to implement using Docker and Symfony.
So using Behat + Selenium or Behat + BrowserStack you will have a top test environment.

Enjoy!

References

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s