Creating Tests for our First Feature

Introduction

In this section, we will finally begin creating our actual test scenarios and coding them.

The website we will be testing is an Angular website called https://www.zagat.com, a US based website for national restaurant reviews and finding recommended places to eat in any given national location.

We will be essentially testing two different features.

The first feature will contain test scenarios around finding a restaurant to eat at, whilst the second feature will contain test scenarios around signing up to their email newsletter.

Let’s begin 🙂

Creating the first Cucumber Scenario

When creating our BDD scenarios, we want to keep them simple and write them in a declarative style, sticking simply to the business logic, rather than writing them in an imperative style.  A great article on this can be found at http://itsadeliverything.com/declarative-vs-imperative-gherkin-scenarios-for-cucumber.

We also want each test scenario to be small, clean and atomic. This means that ideally each test scenario should only test one specific thing and we should try to avoid repeating flows in our tests.

  1. In Visual Studio Code, open up the already created ‘find_a_restaurant.feature’ file and add the following description of the feature along with our first test scenario, like below…

Creating the undefined step definitions

If we now run our tests with npm test we will see output like below in the console…

In the current version of Serenity/JS, the ability to print the pending step definitions in the console has not been implemented yet (as of 03/02/2019).  You can track progress of this at https://github.com/serenity-js/serenity-js/issues/358.

I’ve spoken to Jan Molak, the creator of Serenity/JS and there’s some good bug fixes and features being added soon. If you fancy helping in the ongoing testing of Serenity/JS, please report issues at https://github.com/serenity-js/serenity-js/issues.

For now, we can use the workaround that we have already added in our ‘protractor.conf.js’ file that outputs the pending steps snippets in to a ‘pending-steps.txt’ file…

If we open up our pending-steps.txt file, we will see all of our pending steps like below…

According to https://cucumber.io/docs/gherkin/step-organization/, step definitions can be named whatever we want, but it is generally good practice to group them by major domain objects and to avoid mapping step definition files to features, as that would be an anti pattern.

Additionally, one way we could group step definitions is by user roles. For this blog series, I have decided to take this approach, hence why we named our first step definition file ‘foodie.steps.ts

‘Given that Frank decides to use Zagat to find recommended restaurants’

  1. Copy the pending ‘Given’ step definition from the ‘pending-steps.txt’ file and add it in to the ‘foodie.steps.ts‘ step definition file, along with appropriate imports, like so…
  2. Next, let’s edit the step definition a bit so that it can take any actors name as a {word} parameter by using a Cucumber expression and then also use an arrow function instead (see https://cucumber.io/docs/cucumber/cucumber-expressions/ for more info on Cucumber expressions)…

‘When he searches for all restaurants in his area of San Diego’

  1. Copy the pending ‘When’ step definition from the ‘pending-steps.txt’ file and add it in to the ‘foodie.steps.ts’ step definition file, updating any necessary imports, with the whole thing looking like below…
  2. Next, edit the ‘When’ step so we can use either he/she and his/her, as well as accept any location as a string parameter and use an arrow function again…

‘Then a list of recommended restaurants in San Diego are returned to him’

  1. Copy the pending ‘Then’ step definition from the ‘pending-steps.txt’ file and add it in to the ‘foodie.steps.ts’ step definition file, updating any necessary imports, with the whole thing looking like below…
  2. Next, edit the ‘Then’ step so we can use either him/her, as well as accept any location as a string parameter and use an arrow function again…

Please note that due to the way arrow functions work, you will need to remove the braces/curly brackets when using them.  If you instead decide to not use arrow functions, then you can keep the braces/curly brackets and add a return statement to return your step.

Setting up our actors

We will now create an exportable Actors class that returns an actor who has the ability to BrowseTheWeb.

In this blog series, we will only be interacting with a web interface, so this is fine for our needs.

In other systems, you might have multiple interfaces that need to be interacted with, such as an API layer or a messaging queue etc.

  1. With the project open in Visual Studio Code, right-click on the ‘screenplay’ folder and add a new file called ‘Actors.ts’
  2. Open up the ‘actors.ts’ file and add the following to return an actor who has the ability to BrowseTheWeb, as mentioned above, like so (as you can also see below, you could easily add multiple abilities if necessary for interacting with multiple interfaces)…

Setting up our global configuration

We will now create a ‘setup.ts’ file that will contain all of our global setup and configuration.

For now, we will add a default Cucumber timeout of 10,000 milliseconds (10 seconds) as well as an ‘engage()‘ call that instantiates our Actors class 🙂

  1. With the project open in Visual Studio Code, right-click the ‘support’ folder and add a new file called ‘setup.ts’
  2. Open up the ‘setup.ts’ file and add the following so it looks like below…

Defining the Step Definitions

‘Given that Frank decides to use Zagat to find recommended restaurants’ step

  1. Open up the ‘foodie.steps.ts’ file and add the following in the ‘Given’ step to call an actor (who has the ability to browse the web) to the stage and attempt to navigate to the Zagat (/national) home page…

It is worth noting that the ‘actor.attemptsTo(..)‘ returns a promise and Serenity/JS handles a lot of the promise chaining behind-the-scenes, meaning that in a lot of cases, we don’t need to use async/await or callback()’s in order to execute our steps and lines of code in order.

‘When he searches for all restaurants in his area of San Diego’ step

Now that we have called the actor, that actor will remain in the ‘spotlight’, so we can use this actor by adding an import for ‘actorInTheSpotlight’ like so…

If we break this step down in to any tasks that need to be performed, we could say that the actor in the spotlight could attempt to search for recommended restaurants in their given area of ‘San Diego’

Let’s add this in to our step now

  1. Edit the ‘When’ step in the ‘foodie.steps.ts’ file like below…

    You will notice that the ‘SearchForRestaurants’ task gives us an error as we haven’t created it yet, so let’s do that next
Adding the ‘SearchForRestaurants’ task
  1. With the project open in Visual Studio Code, right-click the ‘screenplay’ folder and add a new sub-folder called ‘tasks’
  2. Right-click the ‘tasks’ folder and add a file called ‘SearchForRestaurants.ts’
  3. Open up the ‘SearchForRestaurants.ts’ file and add the following task to search for recommended restaurants in a given location like below…

    You will notice that an error is coming up as we don’t yet have a ‘ZagatFindAPlaceWidget’ class yet, so let’s implement that next 🙂
Adding the ‘ZagatFindAPlaceWidget’ class
  1. With the project still open in Visual Studio Code, right-click the ‘screenplay’ folder and add a new sub-folder called ‘ui’ that will store our UI sections (or widgets) of our web application under test
  2. Right-click the ‘ui’ folder and add a new file called ‘ZagatFindAPlaceWidget.ts’
  3. Open up the ‘ZagatFindAPlaceWidget.ts’ file and add the following so it is an exportable class that contains the two elements we need to interact with, like below…
  4. Now let’s go back in to the ‘SearchForRestaurants.ts’ file and add an import for the ‘ZagatFindAPlaceWidget’ class…
  5. Next, go back in to the ‘foodies.steps.ts’ file and add an import for the ‘SearchRestaurantsFor.ts’ file…

    The whole thing should look similar to below at this point…

‘Then a list of recommended restaurants in San Diego are returned to him’ step

In this step, we will essentially be asking a question to verify that we see the correct results, through the use of a couple of assertions

Let’s add this in to our step now

  1. Edit the ‘Then’ step in the ‘foodie.steps.ts’ file like below (I have added comments to explain our questions/checks)…

    You will notice that an error is coming up as we don’t yet have a ‘ZagatPlacesWidget’ class yet, so let’s implement that next 🙂
Adding the ‘ZagatPlacesWidget’ class
  1. With the project still open in Visual Studio Code, right-click the ‘ui’ folder and add a new file called ‘ZagatPlacesWidget.ts’
  2. Open up the ‘ZagatPlacesWidget.ts’ file and add the following so it is an exportable class that contains the search results element we want to use, like below…
  3. Next, add an import in for the ZagatPlacesWidget class in our foodie.steps.ts file, with the whole thing looking like below now…

Creating the second Cucumber Scenario

For our next test, let’s do an unhappy path scenario where Frank searches for all restaurants in a location that is invalid and does not actually exist

  1. In Visual Studio Code, open up the ‘find_a_restaurant.feature’ file again and add the following second test scenario, with the whole thing looking like below…

    You may notice that we have re-used our ‘Given’ and ‘When’ steps here, with only a new ‘Then’ step definition being added. Let’s add the new ‘Then’ step now 🙂

Creating the undefined step definition

If we run npm test again, we should get the pending ‘Then’ step definition output in our ‘pending-steps.txt’ file, like so…

  1. Copy and paste the above pending step definition in to the ‘foodie.steps.ts’ file, with the whole thing looking like below…

Defining the step definition

  1. Add the following in the pending step definition to ensure that we see the element with the text ‘no places found’, with an arrow function instead like so…

    You will notice we get an error for ‘NoReturnedResults’, lets create an exportable constant for that now 🙂
Adding the ‘NoReturnedResults()’ const
  1. Right-click the ‘screenplay’ folder and add a new file called ‘NoReturnedResults.ts’
  2. Open up the ‘NoReturnedResults.ts’ file and add the following to get the text of the ‘noPlacesFound’ element…

    You will notice that the ‘noPlacesFound’ gives an error as the element does not exist yet, let’s add it now
  3. Open up the ‘ZagatPlacesWidget.ts’ file’ and add the following ‘noPlacesFound’ element, with the whole file now looking like below…
  4. Finally, go back to the ‘foodie.steps.ts’ file and add in an import for the ‘NoReturnedResults’ const, like so…

    The whole thing should look like below now…

If we now run npm test, it should run both our tests and they should both pass 😀

In the next section, we will start adding tests for our newsletter signup feature

Liked it? Take a second to support Thomas on Patreon!

Previous Article

Next Article

2 Replies to “Part 4. Creating Tests for our First Feature”

  1. I have everything set up exactly as you have here, but when I run npm test, i get “Frank can’t BrowseTheWeb yet. Did you give them the ability to do so?”

    And I’ve checked and double checked that I’ve followed your steps exactly, and everything looks like I did it right. I’m very lost, wondering why this is happening.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.