Karate: Multiline afterScenario with assertions [duplicate] - karate

This question already has an answer here:
Is it possible to use karate 'match' inside conditional statement?
(1 answer)
Closed 1 year ago.
I have the next code:
* def deleteCallResult = call read('classpath:somepath#deleteCall') { id: #(id) }
Then match deleteCallResult.responseStatus == 204
* def getCallResult = call read('classpath:somepath#getCall') { id: #(id) }
Then match getCallResult.responseStatus == 404
Then match getCallResult.response.title == "Not Found"
And would like to put it in afterScenario snippet (without moving it into a separate feature file):
* configure afterScenario =
"""
function(){ ... }
"""
Does Karate syntax allow to do it? Did not find examples

You may be able to do this if you use the JavaScript match syntax explained here: https://stackoverflow.com/a/50350442/143475
And you can try to call a #ignore Scenario in the same feature. The line below is JS. There is an improvement to this that will be in the next version.
karate.call('same.feature', { some: 'arg' });
But consider what you are asking as not supported. Note that trying to write "clever" tests like this typically lands you in trouble. It is recommended that you keep your main test flows simple.

Related

Getting "not defined" error while calling a feature file from another feature file (Karate framework)

Whenever I try to call the feature file(one.feature) from another feature file(starter.feature), I am not able to access the info(caller) in specific scenario
But It is working fine and I am able to access info(caller),
when I run one.feature file standalone.
when I run one.feature file through Junit test case.
CODE WORK-FLOW :
In one.feature :
In the scenario(Scenario: matching two Strings) we are trying to match two Strings and configuring afterScenario JS function to get the info of the scenario and If the info contains any errors, The function will call the other scenario(Scenario: getting caller details) with info details assigned in the variable caller
starter.feature :
Feature: call another feature
Scenario: calling feature file
* def callFeat = call read('one.feature')
one.feature :
Feature: get details
Background:
* configure afterScenario =
"""
function() {
var info = karate.info;
if(info.errorMessage){
karate.call('one.feature#CallCaller', { caller : info });
}
}
"""
#CreateErrorMsg
Scenario: matching two Strings
* def Text1 = 'Hey'
* def Text2 = 'Hello'
* match Text1 == Text2
#CallCaller #ignore
Scenario: getting caller details
* print caller
Can't you use karate.log from the JS function rather than calling another scenario to just print the info? Also, note that karate.info has been deprecated from 1.0 version of Karate and replaced by karate.scenario - https://github.com/karatelabs/karate/wiki/1.0-upgrade-guide#karateinfo-deprecated
* configure afterScenario =
"""
function() {
var info = karate.info;
if(info.errorMessage){
karate.log(info.scenarioName);
}
}
"""

Is it possible to use embedded expressions inside an afterScenario block?

I have multiple scenarios in a feature file. At the end of each scenario, I need to "clean up" before the start of the next scenario. My clean up function requires a json object to be passed in. Each scenario has a different object. Therefore, I need to use embedded expression, so dynamic data gets erased.
My setup is like this:
* configure afterScenario =
"""
function(){
var deleteData = { customerData: '#(portfolio)' };
karate.call(deleteData.feature#deletePortfolio', deleteData);
}
And scenario may look something like this:
// here we get a brand new, unused "portfolio" value from a related function.
* table customer1
|portfolio | status |
|portfolio | 200 |
* call read(random.feature#random) customer1
So at the end of the above scenario, I expect the afterScenario to kick in and delete the "portfolio" variable value for that scenario. However, because it's a Java interop inside the afterScenario block, it doesn't recognize Karate's embedded expressions. Any way around this?
The moment you are within a JS block, you are "out" of Karate. So embedded expressions don't work, but "JS style" expressions work.
Read this once to be more clear about this: https://github.com/karatelabs/karate#karate-expressions
So this will work:
* configure afterScenario =
"""
function(){
var deleteData = { customerData: portfolio };
karate.call('deleteData.feature#deletePortfolio', deleteData);
}
Or even:
* configure afterScenario = function(){ karate.call('deleteData.feature#deletePortfolio', { customerData: portfolio }) }
One more tip, karate.get('someVariableName') can get you the value of any variable, any time.
And I do think you are over-engineering your tests, please don't:
https://stackoverflow.com/a/46080568/143475
https://stackoverflow.com/a/60944060/143475
https://stackoverflow.com/a/54126724/143475

How to pass argument to karate.call [duplicate]

This question already has an answer here:
Calling feature file with multiple arguments
(1 answer)
Closed 1 year ago.
I am passing argument to karate.call as following:
...getting object by name
* def id = response.content[0].id
And eval if (size(response.content) == 1) { karate.call('delete/delete-object.feature', id); }
However, I am getting: unexpected feature call arg type: class java.lang.String. Should I somehow cast the argument at first?
I am just creating the scenario which should at first delete the object if existing. Deletion I am doing in delete-object.feature.
Please read the docs, the (single) call argument always has to be a JSON or array of JSONs (unless it is a call to a JS function): https://github.com/intuit/karate#calling-other-feature-files
It is easy to shape, e.g. : * def arg == { id: '#(id)' }
Just for the sake of reference, I'm posting a solution here.
* def id = response.content[0].id
And eval if (size(response.content) == 1) { karate.call('delete/delete-object.feature') { id : '#(id)' } ; }
Inside delete-object.feature file, just use id to access that value. Keep in mind that there's a strange behavior in karate. If you pass in an object such as " { car: '#(carobject)'} ", you cannot access that object by calling "car" inside the feature file you pass this object in. You need to use the object name itself, not the attribute name. So, in this case, it would be "carobject". Hope that helps

Using karate built-in functions and api

Ok, so I am going to explain what my scenario is and how I have implemented it using karate. But i am looking for a better way of doing things which make my tests more readable and also want to use karate's api rather than too much javascript.
Scenario: I have a filter parameter for the api endpoint that i am testing and this filter param takes a json object of key-value pairs. So i have done the following:
Have created a filter-template.js as below:
function() {
var filter_params = {
jobId:null,
customerIds:[],
filterValues1:[],
filterValues2:[],
};
return filter_params;
}
I read this template in my karate scenario and because this is a template, at runtime i set the values in this template and run the test. I will have different values for key-value pairs so each test will set its own filter condition.
This is done by writing custom js function that takes the template as argument and also the filter condition values (referring to arg function argument below), sets the passed conditions to specific key's and then returns json object. Code below:
Scenario: Set filter scenario
* def filter_template = call read('filter-template.js')
* def filter_vals_list = [1001,1002]
* def filter_condition = { cnd1: 'foo', cnd2: '#(filter_vals_list)' }
* def setFilter =
"""
function(arg) {
var i;
var filter = arg.template;
filter.jobId = arg.condition.cnd1;
for(i=0;i<arg.condition.cnd2.length;i++)
{
filter.filterValues1.add(arg.condition.cnd2.get(i));
}
return filter;
}
"""
* def getFilter = call setFilter { template: '#(filter_template)',
condition: '#(filter_condition)' }
I then pass the getFilter as a param to my api request.
What I am hoping to understand is:
How can i get away from using JS loops above when setting filter?
Use karate's in-built functions like karate.map(), karate.forEach() to simplify the tests.
Any better approach if possible on tackling this scenario.
Help and guidance much appreciated.
From what I understood, I have simplified your scenario below:
* def filter_vals_list = [ 1001, 1002 ]
* def job_id = 'foo'
* def filter_template =
"""
{
jobId: '#(job_id)',
customerIds: [],
filterValues1: '#(filter_vals_list)',
filterValues2: [],
}
"""
Let me know if I missed anything. Please refer embedded expressions: https://github.com/intuit/karate#embedded-expressions
Now, you can easily use a re-usable JSON for the template, by changing the last step to the below, and yes - embedded expressions work even in re-usable JSON files !
* def filter_template = read('filter-template.json')
You may get even better ideas once you try the data-driven Scenario Outline. So hope that makes it clear how you are un-necessarily complicating things with JS ! You don't even need karate.map() etc.

Is there way in karate to get the assertion status like pass or fail in variable for further processing in java?

In feature file i am writing assertion as
match response contains {token_type: '#string' }
Is there a way to get the output status of the above assertion value like true or false,
so that i can extend that into java for further processing?
The whole point of Karate is to avoid Java, and I don't recommend doing anything else.
But since you insist, if you want to get the equivalent of the above, just do something like this. Yes, you have to use "normal" JS fundas.
* def response = { token_type: 'foo' }
* def isString = typeof(response.token_type) === "string"
* match isString == true