All Ruby on Rails Node JS Android iOS React Native Frontend

How to eliminate duplicated code in Rspec and Capybara specs

When our test suite grows we start to notice that some parts of our code get duplicated. Duplicated code decreases readability and is hard to manage since eventual changes or fixes have to be propagated to all duplicated parts. Solution to such situation is to keep your code DRY meaning ‘Don’t repeat yourself’. This is one of the most basic programming  principles that we should also use while writing specs for our applications.

Below I will show you some examples on how to DRY out your code and get rid of duplicates.

 

Put repeating lines to before block

code to improve


In these two specs we can see that first four lines are duplicated. This should give us a signal that we should make this code DRY.


Dry code

We can move these four lines to the before block. Thanks to this, commands from the block will be executed at the beginning, before each scenario thus making our test DRY and readable.

 

Put repeating lines to methods

Code to improve


In this situation we can see the same lines in the middle of the specs. Because first few lines of the specs differ from each other, we can’t use the before block.


Dry code


We create a method with all of the repeating code inside so later we can refer to this method in the test. In this case we also supply page object as a parameter to the method.

 

Expect to change from to

Code to improve


In the spec we check how the number of elements on the page changes after a certain action. We see repeated lines differing only when it comes to the expected number.


Dry code


To fix this duplication we can use method ‘expect to change from to’. In this case, we check the number of elements on the page, then we expect that clicking on a button on the page will change the number of elements from x to y. It is worth noticing that we put all of the code in blocks { }  . Thanks to this, actions are not performed chronologically, but in the order visible on the screen - at first the change block is evaluated to check for the initial value, then the expect action is triggered and lastly the change block is evaluated again to see if the from and to values are as expected.

 

Expect to change by

Code to improve


In this case, at the beginning, we save the number of elements on the page, we delete one element and then check the number of elements after the action. At the end, we expect that the number of elements on the page after the action is one less than before the action. This spec is not bad but we can improve it.


Dry code


We expect that deleting an element on the page, changes the number of elements by -1.

What is the difference between ‘expect to change from to’ and ‘expect to change by’?

In the first function we are interested in the initial number of elements on the page and in the second we are only interested if it changes by the given number (initial state doesn’t interest us).


Loops in scenarios - each

Code to improve

This is a classic situation in which we check the display of elements on the page which differ only by name. Checking each element individually causes many repetitions.


Dry code


We create an array containing the names of repeating elements. We call each loop on the array so that every element of the array becomes our argument. Within the loop we check if given element is present on the screen.


another way of initializing an array


Creating an array using %w notation.


Loops in scenarios - times

Code to improve


In this spec we want to test creation of an empty song list. To do so we are going through the song list form by clicking the 'Next' button on 4 subsequent form pages without interacting with the form itself. This causes a lot of repetition.


Dry code


Times loop allows us to easily perform the same action set amount of times. We are using the times loop instead of the each loop because we don’t have a collection that we want to operate on and our action doesn’t need to be parametrized - we just want to simply repeat a plain action, like clicking on the button, N number of times.

 

Loops around scenarios

Code to improve

We have two identical scenarios that differ only in the type of user.


Dry code

We can also do loops around whole scenarios. In the array we set list of users which we refer to when creating a given user. On the title of scenario we used interpolation to be able to distinguish between the scenarios. Also notice that the user roles that we put into the array match the names of the factories that we are using - this is crucial to be able to create different users per scenario.

 

Loops in the page objects

Code to improve

The problem in this spec is that the page object elements only differ by the name in the selector.


Dry code


We can use the loop also during adding page objects. We create an array with repeating elements. Then we use each loop with an argument. Within the loop we define each element using the arguments from the array - each argument is inserted as an element name and also interpolated into the selector.


create_list

Code to improve

We create each user separately causing a lot of repetitions.


Dry code

The create_list is a factory bot (previously known as factory girl) method that creates a specific amount of users and returns them as an array. Then again we use each loop to check if  all users are properly displayed on the page.


Shared example

Code to improve

We have several very similar tests, differing only in the type of user, email and password.


Dry code

When you have multiple specs that describe similar behavior, it might be better to extract them to shared examples.  We include shared examples in our specs using one of these functions:

  • include_examples "name" - include the examples in the current context
  • it_behaves_like "name" - include the examples in a nested context
  • it_should_behave_like "name" - include the examples in a nested context
  • matching metadata - include the examples in the current context

In our specs we create shared example with variable user and then in every spec we call shared example and supply a user as its argument. This way the user variable in shared example will be set to the user that we supplied to given shared example.


We can also create users earlier through let and later  indicate by argument which use we want to check.


Splat operator

Code to improve

We create several users with specific and long data stored in hashes. A part of user data is identical for every user.


Dry code

In our spec we create a let with the repeating data stored in hash. Then we create each user separately and use our common data with a double splat operator. Thanks to the double splat operator our repeated data will be ‘unpacked’ and supplied to each method just like the rest of the parameters.

 

The above examples are just suggestions for improving the code. Using these techniques won’t always increase the readability of the code, you have to do it with caution. But I think that in most cases they will reduce the amount of code and make it easier to maintain.

Let's make the world better for everyone - join us
New Call-to-action
READ ALSO FROM QA
Read also
Need a successful project?
Estimate project or contact us