Using path paramater in karate URL- special character - karate

I have to use this value as path parameter 6425716f2f8541d5afd52a6e7321743b%2Fv3
It should look like this
GET https://abc123.com/policyNumber/6425716f2f8541d5afd52a6e7321743b%2Fv3
However it is getting changed to 6425716f2f8541d5afd52a6e7321743b%252Fv3 when I do not use encoding. <Changes %2 to %25>
I have tried this approach as well:
* def encoded = 6425716f2f8541d5afd52a6e7321743b%2Fv3
* def id = java.net.URLDecoder.decode(encoded, 'UTF-8')
And path 'abc/v3/policies/'
* header Content-Type = 'application/json'
And path id
However in the above case I get 6425716f2f8541d5afd52a6e7321743b/v3<Removed the %2F and changed to />
I am using version <karate.version>0.9.5</karate.version>
How do I handle this encoding for path parameter?

First we don't support old versions. The rules are explained here: https://github.com/karatelabs/karate#path
I tried this in the latest version (1.3.0)
* def encoded = '6425716f2f8541d5afd52a6e7321743b%2Fv3'
* def id = karate.urlDecode(encoded)
* print id
* url 'https://httpbin.org/anything'
* path id
* method get
Request sent:
1 > GET https://httpbin.org/anything/6425716f2f8541d5afd52a6e7321743b/v3
1 > Host: httpbin.org
1 > Connection: Keep-Alive
1 > User-Agent: Apache-HttpClient/4.5.13 (Java/17.0.4)
1 > Accept-Encoding: gzip,deflate
You can confirm from the server response that it is handled correctly.
You can also try this which seems to work the way you want (but I personally think is not what your server expects)
* def encoded = '6425716f2f8541d5afd52a6e7321743b%2Fv3'
* url 'https://httpbin.org/anything' + '/' + encoded
* method get
Result:
1 > GET https://httpbin.org/anything/6425716f2f8541d5afd52a6e7321743b%2Fv3

Related

Send a second request nb of times as nb of items returned for first request using Karate

I need to send 2 requests in a Scenario Outline. Ex:
Background:
* url 'someurl'
Scenario Outline:
* path <owner_id>, 'cats'
* method get
* status 200
here I need to get ids of cats from response as {"cats": [{"cat_id": "xx"}, {"cat_id": "yy"}...]}
* path <owner_id>, <cat_id>, 'kittens'
* method get
* status 200
Examples:
|owner_id|
|bill_id |
|kate_id | and so on
Is it possible to send the second request (kittens' retrieving) for each cat_id from the first request and this for each owner_id?
I tried another way:
Background:
* url 'someurl'
Scenario Outline:
* def cats = call read('GetCats.feature')
then store cat_ids as here:
* def catsIds = cats.c[*].id (I get an error "javascript evaluation failed: cats.c[*]id, <eval>:1:6 Expected an operand but found *")
OR
* def catsIds = karate.mapWithKey(cats.c[*].id, 'cat_id')
* path <owner_id>, <cat_id>, 'kittens'
* method get
* status 200
Examples:
|owner_id|
|bill_id |
|kate_id | ...
And here is GetCats.feature
Scenario:
* url 'someurl'
* path 'cats'
* method get
* status 200
* def c = response
I was thinking about karate.repeat but can we use it for this case?
Anything is possible, you can call other features, all data is in scope etc.
You made a mistake here, note the $:
* def catsIds = $cats.c[*].id
Refer: https://github.com/intuit/karate#get

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

How can I set base API? I see double quotes getting added from the first API's response

Given path '/api/metrics/product/ABC'
When method get
* def id = get response
* print id
* def basePathProducts = '/another/api/' + id + '/param'
Given path basePathProducts
When method GET
Then status 200
12:59:28.447 [main] INFO com.intuit.karate.StepDefs - [print] "5ca627bf3edd851238e59c9e" Apr 16, 2019 12:59:28 PM org.glassfish.jersey.logging.LoggingInterceptor log SEVERE: 2 * Sending client request on thread main 2 > GET
http://localhost:8080/API/ANOTHER/API/%225ca627bf3edd851238e59c9e%22/PARAM
I think you are overcomplicating things and you missed that the path syntax is designed for what you commonly need to do.
Don't def basePathProducts and do this, see how the id variable can be easily plugged into a path:
Given path 'another', 'api', id, 'param'
Your post is really hard to comprehend.
Try using
Given url yourURLVariable + 'another/api/'+ id + '/param'
Refer to this for more information : https://stackoverflow.com/a/54477346/10791639
Edit :
There is a problem with your parameter.
* def id = "5ca627bf3edd851238e59c9e"
* print id
Gives :
13:24:19.783 [print] 5ca627bf3edd851238e59c9e
So your variable id is "5ca627bf3edd851238e59c9e" instead of 5ca627bf3edd851238e59c9e
* def newresp = function(id){ return id.slice(1, -1); }
* def id = newresp(response)
I added these to remove the first and last characters from the response which is the double quotes in my case. Thanks for responding guys!

Dynamically set a XML tag value while building payload

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.

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 !