Rest-Assured with Cucumber: Using BDD for Web Services Automation

Rest-Assured with Cucumber: Using BDD for Web Services Automation

Behavior Driven Development (BDD) has become a popular approach in communicating requirements between stakeholders of agile teams. In fact, it’s so effective that it’s also being adopted in automation strategies by using Cucumber to write test scenarios in Gherkin (a non-technical, human readable language) and coupling them with an automation framework so that the scenarios are executable in the form that they are originally written.

While many teams use Cucumber to describe their UI testing, this open source software can be used for web service scenarios as well.

For example, here’s a simple scenario that tests Google’s Books API to get a book by its ISBN. This is written in a feature file using Cucumber.

Each line of the scenario would tie to backend code that actually executes the line. Of course, you can automate this from scratch, but there’s a really cool Java testing framework that has done all of the heavy lifting: Rest-Assured. This framework can be used as a standalone automation solution without Cucumber, but it also uses the Gherkin-style Given-When-Then structure so it lends itself quite nicely to being coupled with Cucumber.

Here are the step definitions that the scenario hooks into to enable the execution:

As you can see, Cucumber and Rest-Assured are a match made in “web services automation heaven”! Both technologies are open source, so are free to download and use. And they both are pretty easy and straight forward to configure, so you’ll be up and running in no time.

Here’s the source code on GitHub.

Happy testRESTing!

Angie Jones
  • toks

    Nice article.. I have a quick question if you dont mind

    If you need to reuse the step definition ‘verify_status_code’ (e.g. Status code is 200) in another step definition, how would you go about it? Reason why I am asking is because the ‘response’ object is set by the previous step definition (e.g userRetrieveTheBookByIsbn) and if you call the ‘verify_status_code’ step definition without the userRetrieveTheBookByIsbn step definition in another step definition class, I believe a null pointer exception will be returned.

    Cucumber tends to support re use of step definition. In other words, if you want to verify the response status code for a service call in another step definition class and you type ‘the status code is’ within your feature file, I believe the already defined step (i.e. in BookStepDefinitions class) would be suggested. If you then re-use this step without using the previous step that actually sets the response object, exception will be returned.

    How would you go about doing this?

    August 31, 2016 at 12:59 pm Reply
    • Angie Jones

      Hi Toks,

      Yes, if you’re going to have multiple step definition files, you’ll need to use dependency injection (DI). There’s several options: PicoContainer, Spring, OpenEJB, etc. If you’re not already using DI, then I recommend PicoContainer. Otherwise, use the one that’s already in use, because you should only have one.

      First thing you’ll need to do is add the dependency to your pom file:

      Next, create a new class that holds the common data. For example:

      Then, in each of your step definition files that you want to use this common data, you can add a constructor that takes StepData as an argument. This is where the injection occurs. For example:

      Then you can use stepData to access all of the common fields needed across your step definition classes. For example, I can split the BookStepDefinitions class into two classes:

      Then in another file:

      This will allow you to share the state of the fields in StepData with all of your step definition files. More on DI for Cucumber is here:

      August 31, 2016 at 8:21 pm Reply
      • toks

        Thanks for your swift reply and your work out here is greatly appreciated. I understand the concept of dependency injection that you’ve explained and I do use it as well. However, if for any reason you have the same step in 2 different feature files e.g. Feature A and Feature B.

        The particular step is defined in the step definition for Feature A and its also available for re-use within Feature B. e.g.

        Feature A
        Scenario: Retrieve book with ISBN
        Given ….
        When …
        Then the status code is 200

        Feature B
        Scenario: Add book to a catalogue
        Given ….
        When …
        Then the status code is 200

        My question is more like, if you have ‘verify_status_code’ in both BookStepDefinitions and SecondStepDefinitions (but ‘verify_status_code’ is implemented in BookStepDefinitions), Would you re-use the step defined in Feature A in Feature B or would you create another step (doing the same thing) with a different name in Feature B (DRY) ?

        Also, I noticed that you moved ‘verify_status_code’ from BookStepDefinitions to SecondStepDefinitions, any reason for this?

        September 1, 2016 at 1:01 pm Reply
        • Angie Jones

          I would definitely reuse the step definition. But I wouldn’t leave it in BookStepDefinitions anymore because now it’s common. I would add a new file CommonStepDefinitions and move the verify_status_code to there so that it’s clear that this particular step is one that is applicable to more than one area. Any other steps that you come across that are common can be moved here as well.

          No reason why I moved this method in my example other than to illustrate the global scope of the steps. 🙂

          September 2, 2016 at 7:52 am Reply
          • Kevin Mandeville

            My belief when it comes to writing tests is to not go too far with the DRY principles. At times it makes sense to not repeat something many times like using constants for fields, etc, makes sense. But you can definitely take it way too far in trying to reuse things across tests. When it comes to writing tests I’m a firm believer in following the DAMP (descriptive and meaningful phrases) and not DRY. In writing tests, the most important piece is making sure you are writing thorough but most importantly, easy to ready, tests. If you take DRY principles too far, it becomes harder and harder to look at a single test and understand exactly what it’s testing, what the inputs are and what the outputs are. Think about it from the perspective of coming in to figure out why a test is failing. The more DRY it is, the longer it may take to diagnose where and why the test is failing. I’m completely ok with repeated things across tests if it means my individual tests themselves are easy to understand. Remember, you won’t be deploying your test code.

            April 11, 2017 at 1:37 pm
          • jagadeesh

            Refactored my code… This article helped a lot..

            May 15, 2021 at 1:28 pm
  • Renzo

    Hey Angie, thank you for the amazing explanation, it has helped me a lot in setting up my own framework. I did have a question though, and hopefully you can help me.

    I have created a StepData class for all my common data to be shared among the stepdefs. However, I’m also using the @Before annotation of Cucumber for setting up my test which can currently be found in all my Stepdefinitions. As they are all the same, I was wondering whether it is ok to move the method to the StepData class?

    January 13, 2017 at 10:05 am Reply
    • Angie Jones

      Renzo, so glad it’s been helpful 🙂 Yes, move the @Before to the common file. You don’t want to have it repeated in multiple files. As a rule of thumb, whenever you find yourself repeating yourself (duplicating code), it’s a sign to refactor and move the code to a central place where it can be shared across.

      January 13, 2017 at 11:31 am Reply
  • Vacha

    Hey Angie,

    Thanks for the wonderful post. Might not be exactly related to Rest assured, but can you give me some starting points on micro-services testing automation framework. I am getting few theoretical concepts but not anything concrete as in how to organize it with BDD-Cucumber like we just did here.

    January 20, 2017 at 10:02 pm Reply
  • callel
    January 27, 2017 at 12:37 pm Reply
  • st

    That was an excellent post and really helpful! I was wondering if you had a chance to implement Serenity with Cucumber-Rest Assured combination. The pico-container DI does not seem to work with Serenity and I am struggling to have the injection done.

    February 9, 2017 at 9:15 pm Reply
  • Minoti Singh

    Thanks a lot.
    Very informative article.
    can you explain me how do we run this project in Eclipse or using cmd prompt for Maven

    March 16, 2017 at 5:30 am Reply
  • Nils

    I am very much thankful to you as this post has helped me a lot in automating web services.
    I have a question on composite/nested service request.
    I am able to get the response for non composite/ non nested service request by using
    response = when().get(“ENDPOINT”);
    But how do I validate composite/nested service request?

    My requirement is I have endpoint say “http://localhost:8080/{company}/{employID}”, now when I hit this main-endpoint it internally makes another Get request to sub-endpoint say “http://localhost:8080/{company}” and get the response_1, depending on this response_1 status/content it will make Get call to the main-endpoint and get response_2.

    Now I want to capture intermediate response_1 and perform some json validation and based on this proceed to main endpoint. Any suggestion is of more help and much appreciated. Thanks.

    March 22, 2017 at 8:54 am Reply
  • Jorge Viana

    When you say “Given a book exists with an isbn of 9781451648546”, you already know this because you had to setup the database somehow prior to running the tests.
    I guess your test should create the book with the desired isbn in the “Given…” step, this way you can freely change the isbn that you want to use in your tests.

    April 18, 2017 at 5:48 am Reply
    • Angie Jones

      Thats one approach to test data management. However, in every application, customers/ testers do not have CREATE ability. This is a perfect example. I’m querying Google’s book API. There is no POST action available for this API.

      Also, in the approach that you mention, there are drawbacks. While it allows greater flexibility, it also increases run time. In a case like this, that might cost an additional second or 2 but those few seconds can add up when you’re dealing with thousands of test. The cost becomes exponential when dynamically creating more complex test data that takes minute(s) to execute programmatically.

      April 18, 2017 at 5:56 am Reply
      • Jorge Viana

        I see what you mean. Thank you for explanation. I always try to learn how to better test my APIs.

        April 18, 2017 at 7:10 am Reply
  • Raveendar Reddy

    It’s a nice post.. but here I would like to see a framework with REST-Assured which is similar to the below framework..
    All I want to write my test cases in feature files. They are competing with REST-Assured.

    July 7, 2017 at 12:46 pm Reply
  • nik

    Business analysts write documentation and then developers implement the code in understandable language for business. i don’t understand why quality assurance engineer write it. I understand, just … ? strange phenomen.

    July 20, 2017 at 12:10 pm Reply
    • Angie Jones

      It’s actually the business analyst, developer, and tester (three amigos) who are supposed to collaborate to write the features.

      January 27, 2019 at 8:35 pm Reply
  • nik

    ccucumber not for quality assurance engineer . sorry. but you are also doing another things

    July 20, 2017 at 1:23 pm Reply
  • Dev Prince

    Awesome post!!!!!!!!!!!

    December 13, 2017 at 12:49 pm Reply
  • Vijay

    Thanks it was easy to understand.
    Query if there more than 1 item how to retrieve and if items has to be checked in jumbled manner.
    Example: Key Value
    items.volumeInfo.industryIdentifiers.type ISBN_13

    February 11, 2018 at 12:03 pm Reply
  • Hemanth

    Connection refused:Connect error message is coming for me

    March 19, 2018 at 4:17 am Reply
  • siva

    Hi Angie Jones,

    First of all thanks alot for great explanation.
    can you explain me how do we run this project in Eclipse using Maven

    March 23, 2018 at 1:15 am Reply
  • siva

    First of all thanks alot for great explanation.
    can you add post request code also for this example.

    March 26, 2018 at 12:18 am Reply
  • Pavan

    Hi ANGIE,
    How do we deal with multiple Json Objects lets say i have json below
    “id” : 12345
    books[0]– data
    books[1]– same structure with different data
    i can use json path which is again static approach like i need to see the json and prepare everytime my json paths, but i wanted to handle it dynamically irrespective of json strucutre

    any insights?

    May 22, 2018 at 5:12 pm Reply
  • Tewari

    can someone tell how to run this project please ?

    June 21, 2018 at 1:32 pm Reply
  • Suresh

    Please follow the Github location for sample code

    July 13, 2018 at 6:27 am Reply
  • G Anene

    Hey Angie,Thank you for putting this together. I have used rest assured a few times in the past but what you have put down here has definitely given me a new insight to using it. Please can you spare me a few minutes to clarify an issue for me. For Post requests, what is the most efficient way to reuse request data? For example, If I run test “A” with a payload name “customerData.json”. If the data required for test “B” is slightly different from Test “A”, do I create a new json payload for test “B” or modify the data used in test “A” at runtime?.

    January 25, 2019 at 4:54 am Reply
    • Angie Jones

      I would create a different payload for each test to keep it clean and minimize confusion.

      January 27, 2019 at 8:38 pm Reply
  • Ankur Malik

    Thanks for a great article.My question on this is:How do we debug it in Eclipse?I am not able to debug it and need help.And by debugging, I mean step into the Step file :)Thanks.

    February 5, 2019 at 5:41 pm Reply
    • Angie Jones

      Put the breakpoint in your StepDef glue code. So when that step is executed, it will go into the corresponding method and stop at your breakpoint.

      February 5, 2019 at 5:49 pm Reply
  • Dmitrii

    What if I need to check a null value?* and response contains|error|null|Will above steps work? Or we will get an assertion error because of NULL value != String null ?

    February 26, 2019 at 1:17 am Reply
  • Rajeev Kumar

    HI ANGIE JONES,Thanks for such a nice tutorial in cucumber and rest assured.i was trying with same example google book libraray which you have mentioned earlier to validate below And response includes the following in any order | items.volumeInfo.title | Steve Jobs | | items.volumeInfo.publisher | Simon and Schuster | | items.volumeInfo.pageCount | 630 | |items[0].id | 8U2oAAAAQBAJ |error :java.lang.AssertionError: 1 expectation failed.JSON path items[0].id doesn’t match.Expected: iterable over [“8U2oAAAAQBAJ”] in any order Actual: 8U2oAAAAQBAJ

    March 24, 2019 at 1:21 am Reply
    • Rajeev Kumar

      If you can help e to fix the problem, it would be great.

      March 24, 2019 at 1:23 am Reply
    • Angie Jones

      instead of doing items[0]. id, you only need

      June 21, 2019 at 10:43 am Reply
      • Rajeev Kumar

        Now i am getting below error.

        Expected: iterable over [“BD”] in any order
        Actual: [BA, ID, DC, ZGK, IG, DM, CG, DP,BD]
        Using below in datatable
        | Response.Data.orgs_other_sys_references.other_sys_code | BD|
        August 7, 2019 at 1:15 am Reply
        • Rajeev Kumar
          Hi Angie,
          I am getting below error now, Could you help me to resolve this.
          JSON path Response.Data.orgs_other_sys_references.other_sys_code doesn’t match.
          Expected: iterable over [“BD”] in any order
          Actual: [BA, ID, DC, ZGK, IG, DM, CG, DP, GAE, BD]
          August 7, 2019 at 6:50 am Reply
  • Pawan Singh

    Discussions underneath the article are immense source of information for setting up this. Very Interactive loving it.. Thanks Angie. Can you share the git location for above framework..

    May 14, 2019 at 10:19 pm Reply
  • Pawan Singh

    I have seen many folks asking how to run this project. Since this is cucumber bases you need to create a runner class and use Junit runner. Below is the runner class.

    @RunWith (Cucumber. class ) @CucumberOptions ( features= “src/test/resources/functionalTests” , //feature file location glue= { “stepDefinitions” }, //step definition location tags= { “@P1” }, // Tags ) public class TestRunner {}Run through command line: Navigate to path where your project resides(pom.xml) and execute : clean test “-Dcucumber.options=–tags @P1” Run through runner class file: Right click on file and select Run ‘TestRunner’
    May 15, 2019 at 12:06 am Reply
  • Dilip agheda

    How do you seperate out the test environment config files? e.g., my base URL is different depending on test,staging and prod. what’s the best way to have a config file that holds base URL and test data depending on environment. (my test data different based on environment too)

    September 12, 2019 at 8:44 pm Reply
  • Marty Kane

    Thanks a lot for this clear and concise example! It helped me get up and running super quickly testing my API with Cucumber. Great work.

    October 3, 2019 at 11:35 am Reply
  • Neha Shree

    Above information is great! I am looking into reporting side of cucumber with rest-assured, do you have any recommendation on that? The basic report doesn’t provide request and response information. It would be very helpful if you can guide me in this.

    October 8, 2019 at 10:26 pm Reply
  • Lorenzo

    Hi Angie, thanks for the helpful post, really appreciate it.

    I have a slight problem and hoping you can help me. I’ve downloaded your code from github and got it running with the test passing. I then tried to modify the code to apply what you mentioned in the first comment regarding dependency injection using PicoContainer, but when I do that I get failures and can’t seem to get it working.
    It looks like the response, json and request which are in the StepData don’t get set? The response coming back is as follows:
    response: {
    “kind”: “books#volumes”,
    “totalItems”: 0
    Are you able to shed some light on why it’s not working and whether it’s possible to get it to work please.Thanks in advance.
    April 20, 2020 at 11:55 am Reply
  • Onkar

    Hello Angie,Appreciate all your hard work, thank you for sharing such useful information, I have a question, the dependency injection using the constructor method – is it thread-safe, I wonder how does it work under concurrency as the common objects or data is shared across multiple steps. can you please explain this part? I hope I am able to convey my question to you, let me know otherwise. Many thanks in advance!

    May 3, 2021 at 3:45 am Reply

Post a Comment