-- EricKessler - 18 Sep 2020

A Tale of Two Tests

From zero to test in multiple languages

This guide will walk through the process of setting up an automated browser test, starting from an empty project and going to a finished and passing test. Each stage of the project will be split into sections for each language so that you can see how each language achieves the same goal in its own way.

Prerequisites

  • Java installed on your machine
  • Ruby installed on your machine
  • An IDE of your choice (optional) or other text editing program
  • A command terminal (your OS shiould have one)
  • A browser (your OS should really have one)
Project Setup

To get our project ready we will need a suitable project structure, a tool to manage our various code dependencies, and a tool to help build and run the project. Commands will be run in a terminal and it is recommended that you start in a separate empty directory for each language.

Running Tests

At this point, you should be able to run your (non-existant) tests.

There's not much to see, it may not be in color (depending on what kind of terminal you are using), and, for newer versions of Cucumber, there is a big message about the new publish feature. Let's take care of all of that.

Run the tests again and the output should be nicer to look at this time around.

Writing the Tests

Because Cucumber features are written in Gherkin, they are the same no matter what programming language is used to implement their execution. Add the following 'search.feature' file to the 'src/test/resources/co/ultranauts/app' folder for Java and to the 'features' folder for Ruby.

search.feature

Feature: Search

  In order to learn All The Things
  As a curious person
  I want to search the internet for information

  Scenario: Search for something
    Given I am on the search page of Google
    When I search for "chocolate milk"
    Then I am shown results related to my search

Running the tests again, you should now see that Cucumber did find and execute the feature but its steps were not defined. Helpfully, Cucumber provides code snippets for steps that are missing so that we can easily define them.

Now when you run the tests, Cucumber will see that the steps are defined but the tests will be in a "not really passing, not really failing" state called "pending". This is a result of all of those pending commands in the step definitions and they are a convenient way to make sure that the person writing a test doesn't just forget to finish it and thereby cause the test framework work to count it as a passing test due to not having any failing assertions.
Automating the Tests

So now we come to the last major tool that we need: some way to control a web browser. Selenium is a popular tool for doing this and it is what we will be using for this exercise. Using Selenium requires two things. It it needs a way for Selenium to talk to the browser and it needs a way for our program to talk to Selenium.

In order to talk to a browser, Selenium uses a driver program that is specific to each different browser. Download the driver for whichever browser you want to use for the test.
  • Chromedriver (for Chrome)
  • Geckodriver (for Firefox)
  • Other drivers (Chrome and Firefox drivers tend to be the most up to date/stable, so I recommend using one of those two instead)

Create a new "drivers" folder in the root folder of your project and put the driver file (e.g. "chromedriver.exe") in that folder.

In order to talk to Selenium, our program needs another dependency added.

You will have to run "bundle install" again in order to use this new dependency.

Now we can move on to the test framework itself. Our first goal will be automating the setup and teardown that needs to happen around each test.
  • A hook that runs once before all of the tests that will configure Selenium to use the diver file
  • A hook that runs before each test that will create a browser instance
  • A hook that runs after each test that will get rid of the browser instance
Now is also when state is going to become important because we will need some way to share information between the different stages of a test, of which these hooks are the first and last.

If you run the tests again, they will still report as having undefined steps but now you should also see a browser being created and destroyed when the test runs and, if you were to have more than one test (feel free to write additional tests in the project for practice) you would see this happen multiple times.

Adding implementations for the steps is the only thing left to do at this point. The browser interactions that we need to add code for are
  1. Navigate to Google's search page
  2. Perform a search
  3. Verify that search results appear
For longer or more complex tests, it is recommend to implement one step at a time and then rerun the test to ensure that the new step is working as expected but the test in this example is simple enough that we can just implement all three steps at once.

Update the files as shown below

Just like we had to use the World object save the browser driver object that we created in the hooks so that the steps could use it, we are saving the search term data that was only defined in the second step so that the third step can also have access to it.

The only other noteworthy thing is the distinction between the two checks in the final step of the test. The first check (waiting for the title bar to change) is a "polling" style check. Many asynchronous tests (which most tests that use a browser are) are written with extra code in order to stabilize an otherwise unpredictable application execution time between a user action and the application response. Although it is verifying that something has happened, it is not the actual verification of the test. Even if both checks were written using assertion style methods, only the second check (asserting a non-zero number of search results) is proving that the applicationbehaved correctly. In other words: The second check is the goal of the test and every other check was just helping us get there more easily.

Success!

And there you have it. Running the tests should now result in all of them passing. Congratulations! Feel free to add a few more tests to the project or even go through the process again using another language in order to see how the project structure and Cucumber framework are different. Every difference found is an opportunity to understand the similarities that each implementation shares.

-- MayaMcKela - 18 Jun 2021 (Added tabs to separate languages)
Topic revision: r17 - 23 Jul 2021, StanVogel
© 2020 Ultranauts - 75 Broad Street, 2nd Floor, Suite 206, New York, NY 10004 - info@ultranauts.co