karate xpath - Can we replace/delete a node - karate

I'm looking to test error messages by modifying a valid xml message file.
I can easily add nodes like this in the scenario:
* def invalidDocumentId = read('Valid.xml')
* set invalidDocumentId /soapenv:Envelope/soapenv:Body = <tis:extraBonus>Extra</tis:extraBonus>
Given request invalidDocumentId
But I'd like to also remove nodes, update node names, change attribute names.
Is there a way to set that or do I need to call Java com.intuit.karate.XmlUtils.

First, removing and adding a node and even attributes should be easy, just use the remove and set keywords with XPath. They will over-write values if needed.
* def base = <query><name>foo</name></query>
* remove base /query/name
* match base == <query/>
* set base /query/foo = 'bar'
* set base /query/#baz = 'ban'
* match base == <query baz="ban"><foo>bar</foo></query>
And the good news is that if you have some really tricky XML manipulation requirements, the string replace syntax comes to the rescue. This is best explained in this other answer on Stack Overflow: https://stackoverflow.com/a/50367134/143475 | https://stackoverflow.com/a/53682733/143475

Related

How to get variable from feature, which is using in karate.repeat?

I need to create 2 items, use get method to check if everything is ok and after that I should delete these items.
I have 1 tc - getItem, which uses 2 helpers (postItem and deleteItem).
For getItem I need to have itemId, which I get from postItem, where this variable is defined. After that I use the same itemId for deleteItem as afterhook. What I do:
Feature:get item
Background:Pre-conditions
* url apiUrl
* call read('classpath:/helpers/features/postItem.feature')
* configure afterScenario = function(){karate.call('classpath:/helpers/features/deleteItem.feature')}
Scenario: Get items
* path '/items/'
And param id = itemId
When method Get
Then status 200
It works but I create only 1 item and delete it correctly because itemId is predefined in postItem and I`m able to re-use it. I saw how to use karate.repeat from HERE but when I do the next
* def item = function(i){ return karate.call ('classpath:/helpers/features/postItem.feature')}
I`m not able to get itemId and as a result not able to delete it. Have tried to use
* print item.response
but it is "null"
So I have 2 questions:
How to get variable from postItem
How to delete each of these created items using afterHook?
May I offer some advice. I would NOT try to create helpers and re-use them like this. Please take some time to read this, and then you may understand: https://stackoverflow.com/a/54126724/143475
I would definitely not use a hook also. Please think of the people who need to maintain your test in the future, they will cry.
Here is how I would write your test. And let me repeat, it is OK to repeat some code when your are doing test automation. For a real, working example, see here.
Background:
* url apiUrl + '/items'
Scenario:
* request {}
* method post
* status 201
* path response.id
* method get
* status 200
* request {}
* method post
* status 201
* path response.id
* method delete
# and so on
Otherwise, the only thing I will say is please refer to the documents on how you can call features and get data back in a loop without using karate.repeat() which should be used only for creating JSON arrays. You can see this answer which has an example and links to the documentation: https://stackoverflow.com/a/75394445/143475
Have found solution how can I do this using DRY pattern + afterhooks.
Feature:get items
Background:Pre-conditions
* url apiUrl
* def item = function(i){ return karate.call ('classpath:/helpers/features/postItem.feature')}
* def createdItem = karate.repeat(2, item )
* table createdItems
|itemId |
|createdItem[0].response.data.id|
|createdItem[1].response.data.id|
* configure afterScenario = function(){karate.call('classpath:/helpers/features/deleteItem.feature', createdItems )}
Scenario: Get all items
* path '/items'
When method Get
Then status 200
It works, but maybe it also can be updated. Im new in this)
So, basically, what I do:
I create 2 items, for get method using karate.repeat with calling postItem feature
I create table with itemId references
Create afterHook with calling deleteItem.feature, which should have argument itemId and I provide created table for this.
And I have scenario, which checks created items
And after that these created items are deleted by afterhooks.
As a result, I have clear scenario, which contains
Pre-conditions --> creating items (preparing data)
Scenario body --> GET method
Post-conditions --> deleting created items and returning to default state.
All of this I do because dont have DB read permission) In an ideal world, preparing data should be done via SQL and deleted as well)
Hope this will help someone)) Also, if you find better solution, feel free to write this here) Tnx

Is it possible to generate dynamic variable names in Karate? [duplicate]

This question already has an answer here:
How to set dynamic value as a key to json string in request
(1 answer)
Closed 1 year ago.
I am making a REST API call which returns a response like this
{"id":"726295ab-d6bc-4f09-8cb7-6f6f54fc9364", "name":"Customer Data"}
I create 5 objects like this and I want to store the ids of all the 5 objects from response in 5 different variables.
I tried using something like
* def catID_<categoryName> = $.id
and provided the name of the object in the Examples section. It works fine most of the times except when the name has spaces in it.
no step-definition method match found for: * def catID_Customer Data = $.id
Is it possible to do something like this?
* def catName = replace all spaces in the name with _
* def #(catName)_id = $.id
or is there a better way to achieve this?
You seem to be doing things Karate is not designed to do, so by default please assume that this is not supported.
Most likely, adding keys to a JSON object is a more elegant approach instead of trying to dynamically hack def. For example:
* def variables = {}
* variables['<someDynamicName>'] = $.id
# then later
* print variables['actual name']
Also note that the '< and >' are not required: https://github.com/karatelabs/karate#scenario-outline-enhancements

Calling feature file within another feature file using tags [duplicate]

We have a feature A with several scenarios. And we need one scenario from that file. Can we call it in our feature B?
No. You need to extract that Scenario into a separate*.feature file and then re-use it using the call keyword.
EDIT: Karate 0.9.0 onwards will support being able to call by tag as follows:
* def result = call read('some.feature#tagname')
To illustrate the answer from Karate-inventor Peter Thomas with an example:
Given a feature-file some.feature with multiple scenarios tagged by a tag-decorator:
#tagname
Scenario: A, base case that shares results to e.g. B
// given, when, then ... and share result in a map with keyword `uri`
* def uri = responseHeaders['Location'][0]
#anotherTag
Scenario: X, base case that shares results to e.g. B
// given, when, then ... and share result in a map with keyword `createdResourceId`
* def createdResourceId = $.id
In another feature we can call a specific scenario from that feature by its tag, e.g. tagname:
Scenario: B, another case reusing some results of A
* def result = call read('some.feature#tagname')
* print "given result of A is: $result"
Given path result.uri + '/update'
See also: demo of adding custom tags to scenarios

Using Karate, I am trying to match two json arrays which are out of order

for e.g.
* def products = [{"ProductCode":"a","UnitPrice":100.0},{ {"ProductCode":"b","UnitPrice":200.0}]
* def inventory = [{"ProductCode":"b","UnitPrice":200.0},{ {"ProductCode":"a","UnitPrice":100.0}]
* match products == inventory
This fails as the order of elements in the array are not matching. How can I tell karate to ignore the order?
You really really should read the docs: https://github.com/intuit/karate#match-contains
* def products = [{"ProductCode":"a","UnitPrice":100.0},{"ProductCode":"b","UnitPrice":200.0}]
* def inventory = [{"ProductCode":"b","UnitPrice":200.0},{"ProductCode":"a","UnitPrice":100.0}]
* match products contains only inventory
The match operation is smart because white-space does not matter, and the order of keys (or data elements) does not matter. Karate is even able to ignore fields you choose - which is very useful when you want to handle server-side dynamically generated fields such as UUID-s, time-stamps, security-tokens and the like.
The match syntax involves a double-equals sign '==' to represent a comparison (and not an assignment '=').

Can we call a scenario from one feature in another using karate?

We have a feature A with several scenarios. And we need one scenario from that file. Can we call it in our feature B?
No. You need to extract that Scenario into a separate*.feature file and then re-use it using the call keyword.
EDIT: Karate 0.9.0 onwards will support being able to call by tag as follows:
* def result = call read('some.feature#tagname')
To illustrate the answer from Karate-inventor Peter Thomas with an example:
Given a feature-file some.feature with multiple scenarios tagged by a tag-decorator:
#tagname
Scenario: A, base case that shares results to e.g. B
// given, when, then ... and share result in a map with keyword `uri`
* def uri = responseHeaders['Location'][0]
#anotherTag
Scenario: X, base case that shares results to e.g. B
// given, when, then ... and share result in a map with keyword `createdResourceId`
* def createdResourceId = $.id
In another feature we can call a specific scenario from that feature by its tag, e.g. tagname:
Scenario: B, another case reusing some results of A
* def result = call read('some.feature#tagname')
* print "given result of A is: $result"
Given path result.uri + '/update'
See also: demo of adding custom tags to scenarios