Overview

In this post we will see What is Karate and How to test Rest API with Karate Framework(BDD).

Karate

Karate is a complete DSL and sits on top of Cucumber-JVM. Unlike other automated API testing tools which require a good amount of coding, Karate works out of the box. Test cases are written in Gherkin style text *.feature file.

A Gherkin file is saved with the “.feature” extension. In general feature file starts with a Tag or Feature keyword followed by a description and Scenarios in the format of Given, When and Then along with some assertions.

Maven Setup

If you are using Maven, you need the two following dependencies.

Note: Since Karate 0.3.0 we may choose which HTTP client to use for running our tests. Apache and Jersey implementations are supported. For this post Apache(karate-apache) is used.

<dependency>
	<groupId>com.intuit.karate</groupId>
	<artifactId>karate-apache</artifactId>
	<version>0.9.6</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>com.intuit.karate</groupId>
	<artifactId>karate-junit5</artifactId>
	<version>1.0.1</version>
	<scope>test</scope>
</dependency>

For Junit 4

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

Gradle Setup

testCompile 'com.intuit.karate:karate-junit4:0.6.0'
testCompile 'com.intuit.karate:karate-apache:0.9.6'

Sample UseCase

Let’s assume we have 2 APIs Save and Get users

Save User:

API: POST /karate/users
Request JSON:
{
    "userName": "test", // @NotBlank
    "password": "test123", // @NotNull
}

Get Users:

API: GET /karate/users?userName=
Request Param (Optional): userName    
Response:

[
    {
        "userName": "test",
        "password": "test123",
        "createdDateTime": "2021-05-16T18:52:26.435"
    }
]

Refer DTO and Controller logic for more in detail.

Get Users Feature file

In Get User test we will write Scenario to

  • validate all User response and status code
  • validate User response and status code when pathParam is passed
Feature: Test User Get Info by UserName

  Background:
    * def baseUrl = karate.properties['baseUrl']
    * url baseUrl

  Scenario: Get All Users
    Given path '/karate/users'
    When method GET
    Then status 200
    * print 'Value of Response: ', response[0].userName
    And assert response[0].userName == 'test'

  Scenario: Get User by userName
    Given path '/karate/users'
    And request { userName: 'test' }
    When method GET
    Then status 200
    * print 'Value of Response: ', response[0].userName
    And assert response[0].userName == 'test'

Save Users Feature file

In Save User test we will write Scenario to

  • validate Save User API with Valid Request and response
  • Verify Save API response when mandatory parameters and not passed or Request JSON format is not correct. Data Tables Example is used here to pass multiple cases in single scenarios.
Feature: Test Save User Detail

  Background:
    # global variable for this feature file
    * def baseUrl = karate.properties['baseUrl']
    * url baseUrl
    * def postJson = {userName:'Hello',password:'123'}

     # call once before any test execution. Useful for Data setup
#    * callonce read('classpath:preCall.feature');

    # call after all scenarios are executed in this feature file. Useful for Data clean up
    * configure afterFeature = function(){ karate.print('post feature logic goes here'); }

  Scenario: Save User Detail

    Given path '/karate/users'
    And request postJson
    When method POST
    Then status 200
    And match response == {'message' : 'success' }

  Scenario Outline: <index> - <useCase>:
  validate different scenarios for bad request

    Given path '/karate/users'
    And request <requestJson>
    When method POST
    Then status <status>
    And match response == <response>

    Examples:

      | index | useCase            | requestJson                        | status | response                                                                                          |
      | 1     | No Request Body    | null                               | 400    | {"timestamp":"#string","status":400,"error":"#string","message":"#string","path":"/karate/users"} |
      | 2     | Wrong Request Body | 'test'                             | 415    | {"timestamp":"#string","status":415,"error":"#string","message":"#string","path":"/karate/users"} |
      | 3     | Empty Request Body | {}                                 | 400    | {"password":"must not be null","userName":"must not be blank"}                                    |
      | 4     | UserName is Null   | {'password': '123'}                | 400    | {"userName":"must not be blank"}                                                                  |
      | 5     | UserName is Empty  | {"userName": "","password": "123"} | 400    | {"userName":"must not be blank"}                                                                  |
      | 6     | Password is Null   | {"userName": "Test"}               | 400    | {"password":"must not be null"}                                                                   |

Keywords

  • Feature: One liner Description about feature
  • Background: Define global variables used across all scenarios written in this feature file
  • karate.properties[‘baseUrl’]: read argument passed from cmd eg: mvn clean test -DbaseUrl=http://localhost:8080
  • callonce: will be called once before any scenarios start execution.
  • configure afterFeature: will be called post all scenarios are executed.
  • Scenario: single test case with single response matching
  • Scenario Outline: for multiple cases must have Examples keyword to add multiple test cases
  • We also can use markers like #null, #notnull, #boolean, #number, #string, #array, #object. Refer here for more detail

Running Tests

Here Karate with JUnit is used. We have options to run test cases in parallel or sequential. In addition, we also have the option to generate a test Report using 3rd party library

import com.intuit.karate.junit5.Karate;
import com.intuit.karate.Results;
import com.intuit.karate.Runner;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import net.masterthought.cucumber.Configuration;
import net.masterthought.cucumber.ReportBuilder;
import org.apache.commons.io.FileUtils;

public class KarateDemoTest {

//    // Run Sequential tests
//    @Karate.Test
//    Karate testAll() {
//        return Karate.run().relativeTo(getClass());
//    }

    @Test
    void testParallel() {
        Results results = Runner.builder().relativeTo(getClass()).outputCucumberJson(true).parallel(5);
        generateReport(results.getReportDir());
        assertEquals(0, results.getFailCount(), results.getErrorMessages());
    }

    public static void generateReport(String karateOutputPath) {
        Collection<File> jsonFiles = FileUtils.listFiles(new File(karateOutputPath), new String[] {"json"}, true);
        List<String> jsonPaths = new ArrayList(jsonFiles.size());
        jsonFiles.forEach(file -> jsonPaths.add(file.getAbsolutePath()));
        Configuration config = new Configuration(new File("target"), "demo");
        ReportBuilder reportBuilder = new ReportBuilder(jsonPaths, config);
        reportBuilder.generateReports();
    }
}

Conclusion

In this post, we took a tour of the Karate Testing Framework(BDD).

For trying out locally download source code from Git and start the application locally. Move to the integration-test folder and execute test cases. Refer to the respective readme for more detail.

You can also refer to generated Reports here for reference.


0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *