kloia Blog

Step-3: Reading data files, Scenario Outline, CSV files, Callers

Written by Selcuk Temizsoy | Jun 9, 2021 11:06:07 AM

Previously I talked about Karate fundamentals and simple usage of the API requests with Karate. This article will be about more advanced topics and include what you need while coding automated scripts and building your own test automation framework. 

While you are writing your automation test scripts, you will need to use JSON models. But should you define all of the required models in the feature? That doesn't sound good, it is not the best practice. Karate has a feature that you can read JSON files directly in the feature file. 

 

           

 

Reading JSON files

Reading JSON files or POJOs is the most complicated work on Java or similar programming languages. But Karate's features make it easy. One simple line of code will read your JSON object. Here is the implementation of the reading JSON file:


{
 "name" : "foo",
 "phone": 19872367759
}

JSON file under data package - modelFile.json


Scenario: Read JSON model from file
    * def jsonModel = read('classpath:data/modelFile.json')
    * print jsonModel

This code block will read the modelFile.json file under the data package and below is the output of the test scripts:


15:07:34.181 [ForkJoinPool-1-worker-1] INFO  com.intuit.karate - [print] {
  "name": "foo",
  "phone": 19872367759 } 

You have printed a JSON file to the console, but your primary task is not printing a JSON file to the console; you will use it for making API requests. Now, let's use the JSON file request body and create a new record on the API. I will use one of the public API, PetStore; you can refer to this document for more information.


{
 "category": {
   "id": 0,
   "name": "categoryName"
 },
 "name": "petName",
 "photoUrls": [
   "someURL"
 ],
 "tags": [
   {
     "id": 0,
     "name": "tagName"
   }
 ],
 "status": "available"
}

JSON file under data package - petData.json


Scenario: Read JSON model from file and post it
 Given url 'https://petstore.swagger.io/v2'
 And path 'pet'
 And def jsonBody = read('classpath:data/petData.json')
 And request jsonBody
 When method POST
 Then status 200
    

Post request with JSON file


1 < 200
1 < Access-Control-Allow-Headers: Content-Type, api_key, Authorization
1 < Access-Control-Allow-Methods: GET, POST, DELETE, PUT
1 < Access-Control-Allow-Origin: *
1 < Connection: keep-alive
1 < Content-Type: application/json
1 < Date: Thu, 18 Mar 2021 13:30:08 GMT
1 < Server: Jetty(9.2.9.v20150224)
1 < Transfer-Encoding: chunked
{"id":9222968140497400482,"category":{"id":0,"name":"categoryName"},"name":"petName","photoUrls":["someURL"],"tags":[{"id":0,"name":"tagName"}],"status":"available"}
16:30:08.721 [pool-1-thread-1] INFO  com.intuit.karate.Runner - <> feature 1 of 1: 
    

Console output

As you see in the example above, Karate read the petData.json under the data package and added it to the request body then sent a post request with it. It produces a very detailed console output and you can easily see the result of your request.


HTML report: (paste into browser to view) | Karate version: 0.9.6
file:/Users/projectPath/target/surefire-reports/karate-summary.html
    

HTML report address

 

You can also find the HTML report in the console output and see the result by pasting it to the browser address bar. Or, you can navigate to the surefire-reports directory under the target folder and find the HTML report.

 

 

HTML report

Scenario Outline

While coding automation scripts in the BDD approach, you should use Scenario Outline structure to achieve Data-Driven testing. You should define your data under the examples table and use them in the scenario with their keyword. Let's create one scenario for scenario outline usage and print the data table to the console.


  Scenario Outline: Data table printing <id>
 * print 'current id is ->', id
 Examples:
   | id |
   | 10 |
   | 20 |
   | 30 |
  

16:57:37.956 [ForkJoinPool-1-worker-1] INFO  com.intuit.karate - [print] current id is -> 10
16:57:37.965 [ForkJoinPool-1-worker-1] INFO  com.intuit.karate - karate.env system property was:  
16:57:37.967 [ForkJoinPool-1-worker-1] INFO  com.intuit.karate - [print] current id is -> 20
16:57:37.972 [ForkJoinPool-1-worker-1] INFO  com.intuit.karate - karate.env system property was:  
16:57:37.973 [ForkJoinPool-1-worker-1] INFO  com.intuit.karate - [print] current id is -> 30
  

Console output of the Scenario Outline

In addition to the example above, you can use table reference names like <id>, ‘<id>’. 

In the HTML report, you may want to see the id as a reference for execution. That's why I have used id in the scenario name to present the id value in the HTML report. 

Now, let’s use this example table structure in a real API request. In the example below, a get request scenario takes parameters from the table and verifies the status.


Scenario Outline: GET request with example table - <id>
 Given url 'https://petstore.swagger.io/v2'
 And path 'pet', id
 When method GET
 Then status 200
 Examples:
   | id  |
   | 250 |
   | 251 |
   | 252 |

 

Reading Data Files

If you have UI automation experience with the BDD approach, you may know that it is impossible to read excel or CSV files directly through feature files. Guess what? It is possible with Karate! You can read the CSV files in feature files and use the data in your scenarios. Let's see how you can implement Data-Driven testing with CSV files through a feature file. 


id
250
251
252

CSV file under data package - data.csv


Scenario Outline: GET request with CSV table - <id>
 Given url 'https://petstore.swagger.io/v2'
 And path 'pet', id
 When method GET
 Then status 200
 Examples:
   | read('data/data.csv')|


HTML report

This is incredibly convenient! You can handle Data-Driven testing with the read method through Scenario Outline. 

Caller

I have shown you the most common cases you will need while coding automation test scripts with Karate. I want to mention one more topic that will save lots of time while creating your framework.

Here is the problem. There is a scenario that is responsible for one test case. And, one of the preconditions of the other scenario is the first scenario. How can you handle this problem? Do you write the first scenario inside of the second one? Well, that is not maintainable. Maybe you can think of putting both scenarios in one feature file and running these scenarios sequentially. But it will make your scenarios dependent on each other. So you need a structure that prevents code duplication, provides reusability, and makes your scenarios independent of each other. It is possible with the Callers structure in Karate. 

Imagine that you have one test case, using one get request, and update the response with a new value. So we need to create a new feature to send a get request, use this request's response and send a put request. First, create a feature file for a get request:


Feature: get caller

 Scenario: get scenario with static id
   Given url baseUrl
   And path 'pet', 250
   When method GET
   Then status 200
   And print 'after get', response
  

callerGet.feature under callers package (baseUrl is petStore URL)

 

Now create a new feature that will call the feature above.


Scenario: caller feature usage
 Given url baseUrl
 And path 'pet'
 And def getFeature = call read('classpath:callers/callerGet.feature')
 And getFeature.response.name = 'newName'
 And request getFeature.response
 When method PUT
 Then status 200
 And print 'after put', response
  

Now, here is the output of the above feature:


10:53:59.941 [ForkJoinPool-1-worker-1] INFO  com.intuit.karate - [print] after get {
  "photoUrls": [
    "string"],
  "name": "doggie",
  "id": 250,
  "category": {
    "name": "string",
    "id": 0},
  "tags": [{
      "name": "string",
      "id": 0}],
  "status": "available"
}

10:54:00.552 [ForkJoinPool-1-worker-1] INFO  com.intuit.karate - [print] after put {
  "photoUrls": [
    "string"],
  "name": "newName",
  "id": 250,
  "category": {
    "name": "string",
    "id": 0},
  "tags": [{
      "name": "string",
      "id": 0}],
  "status": "available"
}
  

The executed Karate script above read the other feature file first and did all of the operations in this caller feature, then ran the remaining script in the current feature. Caller feature sends a get request to the API. The main feature uses the caller’s response, updates one value inside of the response, and sends a put request to update the record with new value.

It is an excellent structure. But if you notice, I have used static parameters, 250 as id in the get request. It is not a good way to use these callers. You should pass the parameters from one to another. Karate allows you to use parameters as well while calling other features. Let's use parameters and see how you can handle this problem. 


Scenario: get scenario with parametric id
 Given url baseUrl
 And path 'pet', id
 When method GET
 Then status 200
 And print 'after get', response
  

Scenario: caller
 Given url baseUrl
 And path 'pet'
 And def getFeature = call read('classpath:callers/callerGet.feature'){id:251}
 And getFeature.response.name = 'newName'
 And request getFeature.response
 When method PUT
 Then status 200
 And print 'after put', response
  

HTML report of caller scenario

I have modified the caller feature to use parameters as an id. Afterward, in the main feature, I called the get caller feature with an id and updated the caller’s response, and added this response as request body to send a put request.

You can pass whatever parameter you want with this approach, and you can use the parameters in the caller features as well. This approach is convenient; it will increase your framework reusability and also the quality of your code. 

 

Conclusion

This article talked about some advanced features of Karate. If you have read through all three posts, you have learned most of the operations with the Karate Framework.

The following article will be about JUnit runners, Cucumber Reports, and command-line parameters.

 

                         

 

Stay on the line and keep learning...

Resources: https://github.com/intuit/karate