Dynamically set a XML tag value while building payload - testing

I am trying automate testing using Karate.
I have a XML payload.
I have a static XML payload which I am readying from a file and I want to call my service in loop.
For each call I would like to replace value for a tag name dynamically.
How would I achieve this?
e.g.
Below is my Main Feature which calls my common feature in loop
Feature: Loop Call
Background:
* def common = call read('classpath:CommonFeatures.feature')
Scenario:
* table table
| payload_file | field_tag | field_value |
| 'HappyPath.xml' | 'car_fuel' | 'Gas' |
| 'HappyPath.xml' | 'car_color'| 'Red' |
* def response = call read('classpath:Car.feature') table
Car.feature
Feature: Common
Scenario:
* print payload_file
* print field_tag
* print field_value
* xml payload = read('classpath:/payload/'+payload_file)
* print payload
* set payload/$field_tag = field_value
This is where I have issue setting the field_tag value.
I have other option to do this like writing a small java script method to replace the tag value or a small java class which use DOMParser or SAXParser to perform the same.
However I would like to know if there is any karate in build way to perform the same.
Also while using java script method to replace the tag value if I am using var parser = new DOMParser(); and it seems DOMParser is not available to use. Is there a way to make this available?

I think if you go through this sample, it will answer all your questions:
https://github.com/intuit/karate/blob/master/karate-junit4/src/test/java/com/intuit/karate/junit4/xml/xml.feature
For example this is how you can substitute values, as well replace a whole chunk of XML (which can include tags) using the set keyword:
Scenario: set xml chunks using xpath
* def req = read('envelope1.xml')
* def phone = '123456'
* def search =
"""
<acc:getAccountByPhoneNumber>
<acc:phoneNumber>#(phone)</acc:phoneNumber>
</acc:getAccountByPhoneNumber>
"""
* set req /Envelope/Body = search
* match req ==
"""
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:acc="http://foo/bar">
<soapenv:Header />
<soapenv:Body>
<acc:getAccountByPhoneNumber>
<acc:phoneNumber>123456</acc:phoneNumber>
</acc:getAccountByPhoneNumber>
</soapenv:Body>
</soapenv:Envelope>
"""
And please don't think of using DOMParser etc, you will be able to do anything you need via Karate syntax.

Thanks to Peter for all help and the examples.
I feel this is the best way to achieve this.
Wrote a small javascript function
"""
* def replaceTag =
"""
function(x){
karate.setXml('temp', x.payload);
karate.pretty(karate.get('temp'));
if (x.field_tag) karate.set('temp', x.field_tag, x.field_value);
return karate.get('temp');
}
"""
and calling the same from Car.feature like below and I get the dynamically replaced payload.
Feature: Common
Scenario:
* print payload_file
* print field_tag
* print field_value
* xml payload = read('classpath:/payload/'+payload_file)
* print payload
* def args = { payload: #(payload), field_tag: #(field_tag), field_value: #
(field_value)}
* print args
* xml payload = call common.replaceTag args
Note: I had to upgrade Karate 0.7.0 version in order to use karate.setXml method.

Related

Scenario Outline for json in feature with Karate

Is it possible to use Scenario Outline as in this mode (which is really great!!):
Scenario Outline:
* print 'hello <name>'
Examples:
| (read('cats.json')) |
but with a json or a list in Background? Ex:
Background:
* json temp = cats_ids (ids that I get from an external job as here [111,222,333...])
or
* def temp = cats_ids
Scenario Outline:
* path id from temp
* method get
...
Examples:
| temp |
Yes, please look at karate.mapWithKey() explained here: https://github.com/intuit/karate#json-transforms
* def temp = karate.mapWithKey(cats_ids, 'id')
Scenario Outline:
* print id

Use variables in json path expressions [duplicate]

This question already has an answer here:
How to use a variable in JsonPath filter
(1 answer)
Closed 2 years ago.
I have a feature file to retrieve the data from csv file for the given parameter. In order to do it, I need to use a variable in the JsonPath expression to retrieve data for given parameter. I tried may ways, but using variable in jsonPath is not working. I'm using 0.9.4 version
I tried the below ways:
* def userId = get[0] testData[?(#.UserType=='${userType}')].UserId
* def userId = get[0] testData[?(#.UserType==userType)].UserId
* def userId = get[0] testData[?(#.UserType=='#(userType)')].UserId(I suppose this can only be used in json/xml)
Below hard coded value works fine:
* def userId = get[0] testData[?(#.UserType=='SuperAdmin')].UserId
Called feature:
Feature: Utility to extract the various types of data from excel datasource
Background:
* def DataUtility = Java.type('com.org.utils.DataUtility')
* def dataUtils = new DataUtility()
* def testData = read('classpath:testdata/TestData.csv')
Scenario: Retrieve userId for a given user type
* def userId = get[0] testData[?(#.UserType=='${userType}')].UserId
Calling feature:
* table params
| userType |
| 'SuperAdmin' |
* def extractedData = call read('DataExtractor.feature') params
* def userID = extractedData[0].userId
I have tried using variables in Json Path.
Example:
String elementPath = "$.[" + i + "].item.value";
In this statement, i is a loop variable. After this pass it to the read api of DocumentContext.
It has worked perfectly fine for me.
I am using JsonPath & DocumentContext from jayway to achieve this.
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;

karate doesn't match xml obtained from function

I have to verify an XML fragment obtained from a function, but matches fails also when should.
To explain my needs, i have to test a web service that in response sends a soap message containing into the body an xml fragment encoded in base64. In my karate test i decode that fragment with a function and verify it with a fuzzy match, but every match fails also when good.
I made a test where the XML 'A' is explicity defined and the XML 'B' is obtained from a function where A == B. Then i define a XML 'C' that should match both, but instead matches only the one explicity defined.
Feature:
Background:
* def buildXml =
"""
function(param){
return '<root><hello>world</hello><foo>bar</foo></root>';
}
"""
Scenario:
* def a = <root><hello>world</hello><foo>bar</foo></root>
* def b = buildXml()
* def c =
"""
<root>
<hello>world</hello>
<foo>#ignore</foo>
</root>
"""
* match a == b
* match a == c
* match b == c
Last match fails, but should pass.
Just one small change, and you are good:
* xml b = buildXml()
Reason: https://github.com/intuit/karate#type-conversion

Can I run example in scenario outline for n times?

I want to run examples for n times , n is the length of data of getHotelid.
Feature: match rates from ds api to pricing api.
Background:
* url ''
Scenario Outline: Calling production assert feature file.
* def DbNew = Java.type('DbNew')
* def dq = new DbNew()
* def activeHotels = <ds_hotel_id>
* def hotel_id = call read('StringConverter.js') { activeHotels:'#(activeHotels)'}
* print hotel_id
Examples:
|dq.getHotelid()|
This is look a like Karate - Not able to run dynamic scenario outline in a loop
Your Examples should be as below,
Examples:
|dq.getHotelid()|
please read this documentation : Karate dynamic scenario

How to get id in responseHeaders location?

For my POST request, in responseHeaders I get 1 < location: /users/123
I would like to print only the id > 123.
When I do * print responseHeaders['location'][0] in my feature file, I get users/123. How I can get only the id?
This is not really a Karate question is it :P
Try this:
* def location = responseHeaders['location'][0]
* def userId = location.substring(location.lastIndexOf('/') + 1)
* print userId
That's right, all the power of JavaScript (or even Java via Java interop) is available to you in Karate !