Monitor and troubleshoot Java applications and services with Datadog: 

>> Try it free!

1. Overview

In this article, we’ll introduce Karate, a Behavior Driven Development (BDD) testing framework for Java.

2. Karate and BDD

Karate is built on top of Cucumber, another BDD testing framework, and shares some of the same concepts. One of these is the use of a Gherkin file, which describes the tested feature. However, unlike Cucumber, tests aren’t written in Java and are fully described in the Gherkin file.

A Gherkin file is saved with the “.feature” extension. It begins with the Feature keyword, followed by the feature name on the same line. It also contains different test scenarios, each beginning with the keyword Scenario and consisting of multiple steps with the keywords Given, When, Then, And, and But.

More about Cucumber and the Gherkin structure can be found here.

3. Maven Dependencies

To make use of Karate in a Maven project, we need to add the karate-apache dependency to the pom.xml:

<dependency>
    <groupId>com.intuit.karate</groupId>
    <artifactId>karate-apache</artifactId>
    <version>0.6.0</version>
</dependency>

We’ll also need the karate-junit4 dependency to facilitate JUnit testing:

<dependency>
    <groupId>com.intuit.karate</groupId>
    <artifactId>karate-junit4</artifactId>
    <version>0.6.0</version>
</dependency>

4. Creating Tests

We’ll start by writing tests for some common scenarios in a Gherkin Feature file.

4.1. Testing the Status Code

Let’s write a scenario that tests a GET endpoint and checks if it returns a 200 (OK) HTTP status code:

Scenario: Testing valid GET endpoint
Given url 'http://localhost:8080/user/get'
When method GET
Then status 200

This works obviously with all possible HTTP status codes.

4.2. Testing the Response

Let’s a write another scenario that tests that the REST endpoint returns a specific response:

Scenario: Testing the exact response of a GET endpoint
Given url 'http://localhost:8080/user/get'
When method GET
Then status 200
And match $ == {id:"1234",name:"John Smith"}

The match operation is used for the validation where ‘$’ represents the response. So the above scenario checks that the response exactly matches ‘{id:”1234″,name:”John Smith”}’.

We can also check specifically for the value of the id field:

And match $.id == "1234"

The match operation can also be used to check if the response contains certain fields. This is helpful when only certain fields need to be checked or when not all response fields are known:

Scenario: Testing that GET response contains specific field
Given url 'http://localhost:8080/user/get'
When method GET
Then status 200
And match $ contains {id:"1234"}

4.3. Validating Response Values with Markers

In the case where we don’t know the exact value that is returned, we can still validate the value using markers — placeholders for matching fields in the response.

For example, we can use a marker to indicate whether we expect a null value or not:

  • #null
  • #notnull

Or we can use a marker to match a certain type of value in a field:

  • #boolean
  • #number
  • #string

Other markers are available for when we expect a field to contain a JSON object or array:

  • #array
  • #object

And there’re markers for matching on a certain format or regular expression and one that evaluates a boolean expression:

  • #uuid — value conforms to the UUID format
  • #regex STR — value matches the regular expression STR
  • #? EXPR — asserts that the JavaScript expression EXPR evaluates to true

Finally, if we don’t want any kind of check on a field, we can use the #ignore marker.

Let’s rewrite the above scenario to check that the id field is not null:

Scenario: Test GET request exact response
Given url 'http://localhost:8080/user/get'
When method GET
Then status 200
And match $ == {id:"#notnull",name:"John Smith"}

4.4. Testing a POST Endpoint with a Request Body

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

Scenario: Testing a POST endpoint with request body
Given url 'http://localhost:8080/user/create'
And request { id: '1234' , name: 'John Smith'}
When method POST
Then status 200
And match $ contains {id:"#notnull"}

5. Running Tests

Now that the test scenarios are complete, we can run our tests by integrating Karate with JUnit.

We’ll use the @CucumberOptions annotation to specify the exact location of the Feature files:

@RunWith(Karate.class)
@CucumberOptions(features = "classpath:karate")
public class KarateUnitTest {
//...     
}

To demonstrate the REST API, we’ll use a WireMock server.

For this example, we mock all the endpoints that are being tested in the method annotated with @BeforeClass. We’ll shut down the WireMock server in the method annotated with @AfterClass:

private static WireMockServer wireMockServer
  = new WireMockServer();

@BeforeClass
public static void setUp() throws Exception {
    wireMockServer.start();
    configureFor("localhost", 8080);
    stubFor(
      get(urlEqualTo("/user/get"))
        .willReturn(aResponse()
          .withStatus(200)
          .withHeader("Content-Type", "application/json")
          .withBody("{ \"id\": \"1234\", name: \"John Smith\" }")));

    stubFor(
      post(urlEqualTo("/user/create"))
        .withHeader("content-type", equalTo("application/json"))
        .withRequestBody(containing("id"))
        .willReturn(aResponse()
          .withStatus(200)
          .withHeader("Content-Type", "application/json")
          .withBody("{ \"id\": \"1234\", name: \"John Smith\" }")));

}

@AfterClass
public static void tearDown() throws Exception {
    wireMockServer.stop();
}

When we run the KarateUnitTest class, the REST Endpoints are created by the WireMock Server, and all the scenarios in the specified feature file are run.

6. Conclusion

In this tutorial, we looked at how to test REST APIs using the Karate Testing Framework.

Complete source code and all code snippets for this article can be found over on GitHub.

Monitor and troubleshoot Java applications and services with Datadog: 

>> Try it free!