Functional tests with puppeteer and gherkin scenarios

For my custom needs, I was looking for puppeteer functional testing suite. As a php developer, I ordinary use behat to write and play my functional tests. But the project I want to test is python written, and I don’t want to install and configure PHP just to run tests. And what about puppeteer and chrome headless engine? Hey, I had an idea: implement the cool gherkin syntax with puppeteer in pyhton.

About Gherkin

Gherkin is a syntax which looks like:

Feature: Use the rest test suite

	Scenario: We can fetch data from an api
		Given I add these headers:
			| accept | application/json |
		Given I prepare a "GET" request to "/todos/1"
		When I send the request
		Then print the last json response
		And the json node "anythng" should not exist
		And the json node "userId" should exist
		And the json node "id" should be equal to "1"

It’s readable by human, (I mean, by non-developer human :)) and it’s really easy to write.

About Puppeteer

Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol, and allows to execute actions in a headless (without visible UI) version of chrome. In nodejs, these tests look like:

const puppeteer = require('puppeteer');
(async() => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.setRequestInterception(true);
  page.on('request', request => {
    if (request.resourceType() === 'image')
      request.abort();
    else
      request.continue();
  });
  await page.goto('https://news.google.com/news/');
  await page.screenshot({path: 'news.png', fullPage: true});

  await browser.close();
})();

Understandable by developers, but a bit ugly (and boring to write)… However, using a real browser as chrome to play functional tests has a lot of advantages:

  • javascript testing
  • speed
  • easy to use

Let’s play with pyppettheater

Pyppettheater is one of my amaziiing idea (and it’s what this article is about). In python, the puppether engine can be used thanks to Pyppeteer. And to parse the gherkin features files, we’ll use gherkin-parser.

Install pyppetheater

First, you have to install the chrome headless engine (under ubuntu):

$ wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
$ sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
$ apt-get -y update
$ apt-get install -yqq unzip sudo python3-pip mysql-client google-chrome-stable
$ wget -O /tmp/chromedriver.zip http://chromedriver.storage.googleapis.com/`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`/chromedriver_linux64.zip
$ unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/
$ pyppeteer-install

And install pyppettheater:

$ pip3 install Pyppetheater

You can now run the pyppet_theater binary:

$ pyppet_theater /path/to/your/yml/or/feature/file

Some example of test

Each test suite is a yml file which contains some parameters (if needed):

parameters:
    rest:
        base_endpoint: https://jsonplaceholder.typicode.com

scenarios:
    0: rest/rest-suite.feature

For a REST API:

For example in the rest/rest-suite.feature:

Feature: Use the rest test suite

	Scenario: We can fetch data from an api
		Given I add these headers:
			| accept | application/json |
		Given I prepare a "GET" request to "/todos/1"
		When I send the request
		Then print the last json response
		And the json node "anythng" should not exist
		And the json node "userId" should exist
		And the json node "id" should be equal to "1"
$ pyppet_theater rest.yml

For a frontend test:

For example, in a game/registration.feature file:

Feature: Create an account on a website

	Scenario: As a visitor, I register on the website
		Given I go on "http://my-website.com"
		And I click on "\#signup-link"
		And I type "player6" in field "\#id_username"
		And I type "somepassword" in field "\#id_password1"
		And I type "somepassword" in field "\#id_password2"
		When I click on "\#register"
		Then I should be on "http://my-website.com"

And run it:

$ pyppet_theater game/registration.feature

For mysql tests:

With a mysql.yml file

parameters:
    mysql:
        db_host: 127.0.0.1
        db_user: root
        db_password: root
        db_name: test

scenarios:
    0: "mysql/mysql-suite.feature"

And, in a mysql/mysql-suite.feature:

Feature: Use the mysql test suite

	Scenario: We can select, update and check existence
		Given the row with "id" equal to "1" in table "some_table" should exist
		And the row with "id" equal to "1" in table "some_table" has "some_key" equal to "some_value"
		Then the row with "id" equal to "1" in table "some_table" should have "some_key" equal to "some_value"
		And the row with "some_key" equal to "some_value" in table "some_table" should exist

	Scenario: We can delete and check existence
		Given the row with "id" equal to "1" in table "some_table" does not exist
		Then the row with "id" equal to "1" in table "some_table" should not exist

And run it:

$ pyppet_theater mysql.yml

You can also use the docker image.

To know more about Pyppettheater, go to the repo!

Written on June 3, 2019