Karate framework: REST API testing made easy!

We all agree that there’s no way of making HTTP requests and navigating the forest of data that is returned to be fun! API testing, be it REST or GraphQL, can be tricky – both from perspectives of the testing code itself and from maintaining it up to date as it evolves over time.

But wait ... there is hope! By using Karate Framework you can overcome these struggles! Karate Framework helps get rid of the boilerplate code which is involved in performing the requests and asserting the response contents. It was born out of a strong dissatisfaction with the current state of solutions that exist.

Karate strives to reduce the entry barrier to writing a test and more importantly, reduces the friction to maintain a test, because of how readable tests become.

Karate and BDD

Karate is built on top of Cucumber! Hence it shares some of the same concepts and provides a descriptive language based on Cucumber that allows tests to be easily written and understood even by non-programmers.

However Karate is not really a BDD Framework! This affirmation is coming from the framework’s author itself, Peter Thomas, who has written a detailed article about this topic, please feel free to read it for more details: “Yes, Karate is not true BDD“.

Showtime

In order to demonstrate a bit of the framework, we need first an accessible API. For that I'm using the api playground project from Best Buy. The project readme details the steps of getting the project up and running in few minutes. After the playground API is running, it is time to get started with the testing.

Dependencies

Using Maven here, we just need to add karate-apache dependency to the pom.xml. We’ll also need the karate-junit4 dependency to facilitate JUnit testing:

 <dependencies>
        <dependency>
            <groupId>com.intuit.karate</groupId>
            <artifactId>karate-apache</artifactId>
            <version>${karate.version}</version>
            <scope>test</scope>
        </dependency>            
        <dependency>
            <groupId>com.intuit.karate</groupId>
            <artifactId>karate-junit4</artifactId>
            <version>${karate.version}</version>
            <scope>test</scope>
        </dependency>        
    </dependencies>

REST Resources

The Api playground project contains 4 endpoints:

  • /products - a subset of ~50,000 products available at Best Buy.
  • /categories - All product categories and their subcategories/path.
  • /stores - A list of all Best Buy store locations.
  • /services - A list of all services available at Best Buy stores.

In this articles, we're going to writes test for:

  • List all existing products
  • Create a new product
  • Get a product (by id)

JUnit Runner

First thing, let's create a package and name it features under src/test/java. This is where all the feature test files will reside. In order to run tests on feature files, we will need one class at the root of our features folder like the one below. This simple class allows us to run our tests with the JUnit framework.

@RunWith(Karate.class)
public class ApiPlaygroundTestRunner {  
}

We may fine-tune which scenarios to include and where to find it using additional configuration but if we keep this test classes package and the scenario file’s path in sync, we don’t need to add anything further than the following two lines to our class.

More detailed informations about naming conventions can be found in the project’s wiki here, detailed information about adding Cucumber options are available here.

Karate Scenarios

Now we can proceed and create our first feature file. Let’s create a file called products.feature under src/test/java/features/products:

Feature: check returned products by the API  
  Background:
    * url 'http://localhost:3030'
    * header Accept = 'application/json'
  Scenario: List products
    Given   path 'products'
    When method get
    Then status 200

This is a very basic test, but it gives a clear insight of the power of Karate! With a single *.feature file using a Gherkin-like language, we're able to define a test against the /products endpoint, performing a GET, asserting that it will return the HTTP status 200.
Karate will interpret your Gherkin file with the power of Cucumber and its built-in StepDefinitions behind the scenes, as well as prepare request calls with all available http methods, deal with json contents, http headers and make assertions.

Let’s add another scenario to explore more advanced tests:

...
  Scenario: Retrieve a specific product
    Given  path 'products/43900'
    When method get
    Then status 200
    And  match response contains { name: #notnull, price: #notnull, description: #notnull, image: #notnull  }
    And  match response.categories contains { id: #notnull, name: #notnull, createdAt: #notnull, updatedAt: #notnull }

In this example, we see how Karate make assertions with responses using the powerful jsonPath matcher. we're even able to navigate to the categories array and evaluate its content without writing complicated matchers or delicate string manipulations.

Let’s look at a final scenario that tests a POST endpoint and takes a request body:

...
  Scenario: Create and retrieve a Product
    Given path 'products'
    And request { "name": "My product", "type": "Super Type", "price": 123, "shipping": 0, "upc": "041345324016", "description": "My super nice aweome product", "manufacturer": "Feo Hero", "model": "QB2400B4Z", "url": "some.url", "image": "some.image" }
    When method POST
    Then status 201
    And def product = response

    Given path '/products/'+product.id
    When method GET
    Then status 200
    And match $ contains {id:'#(product.id)',name:'#(product.name)',type:'#(product.type)',price:#(product.price)}

Amazing, isn't!

That's it folks! This was a very quick introduction to the goodness of Karate framework. I really appreciate the fact that Karate strives to reduce the entry barrier to writing test by making them more human readable. I highly recommend to check out the project wiki for more details.