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 and Configuration
To make use of Karate in a Maven project, we just need to add the karate-junit4 dependency:
<dependency>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-junit4</artifactId>
<version>1.3.1</version>
<scope>test</scope>
</dependency>
Furthermore, in order to have our .feature file in the same directory of the Junit Class, we need to add the following code to our maven
<build>
<testResources>
<testResource>
<directory>src/test/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
<plugins>
...
</plugins>
</build>
This is recommended by the Karate team in order to keep Test and description of the execution side-by-side, and to split .feature files from other test resources.
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:8097/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:8097/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:8097/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:8097/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:8097/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.
@RunWith(Karate.class)
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(WireMockConfiguration.options().port(8097));
@BeforeClass
public static void setUp() throws Exception {
wireMockServer.start();
configureFor("localhost", 8097);
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; charset=UTF-8")
.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.