How to do conditional variables definition on Karate - karate

I had written karate tests for one environment only (staging). Since the tests are successful on capturing bugs (thanks a lot Karate and Intuit team!), there is now request to run the tests on production.
Our tests are graphql-based where most of the requests are query. I wonder if it is possible for us to switch variables based on karate.env we passed on terminal?
Most of our requests look like this:
And def variables = {objectID:"1234566", cursor:"1", cursorType:PAGE, size:'10', objectType:USER}
And request { query: '#(query)', variables: '#(variables)' }
When method POST
Then status 200
I had tried reading the conditional-logic page on github page but haven't yet found a success.
What I tried so far is:
* if (karate.env == 'staging') * def variables = {objectID:"1234566", cursor:"1", cursorType:PAGE, size:'10', objectType:USER}
But to no success.
Any help will be greatly appreciated. Thanks a lot!

We keep our graphql queries & variables in separate json files, but, we're attempting to solve the same issue. Based on what Peter wrote I came up with this, though it will likely get cleaned up before deployment.
Given def query = read('graphqlQuery.graphql')
And def prodVariable = read('prod-variables.json')
And def stageVariable = read('stage-variables.json')
And def variables = karate.env == 'prod' ? prodV : stageV
And path 'api/' + 'graphql'
And request { query: '#(query)', variables: '#(variables)' }
When method post
Then status 200

This should be easy:
* def variables = karate.env == 'staging' ? { objectID: "1234566", cursor: "1", cursorType: 'PAGE', size: '10', objectType: 'USER' } : { }
Here is another hint:
* def data = { staging: { foo: 'bar }, production: { foo: 'baz' } }
* def variables = data[karate.env]
EDIT: also see this explanation: https://stackoverflow.com/a/59162760/143475

Related

Karate : dynamic test data using scenario outline is not working in some cases

I was tryiny to solve dynamic test data problem using dynamic scenario outline as mentioned in the documentation https://github.com/karatelabs/karate#dynamic-scenario-outline
It worked perfectly fine when I passed something like this in Example section
Examples:
|[{'entity':country},{'entity':state},{'entity':district},{'entity':corporation}]]
But I tried to generate this json object programatically , I am getting aa strange error
WARN com.intuit.karate - ignoring dynamic expression, did not evaluate to list: users - [type: MAP, value: com.intuit.karate.ScriptObjectMap#2b8bb184]
Code to generate json object
* def user =
"""
function(response){
entity_type_ids =[]
var entityTypes = response.entityTypes
for(var i =0;i<entityTypes.length;i++ ){
object = {}
object['entity'] = entityTypes[i].id
entity_type_ids.push(object)
}
return JSON.stringify(entity_type_ids)
}
"""

In Karate - Feature file calling from another feature file along with variable value

My apologies it seems repetitive question but it is really troubling me.
I am trying to call one feature file from another feature file along with variable values. and it is not working at all.
Below is the structure I am using.
my request json having variable name. Filename:InputRequest.json
{
"transaction" : "123",
"transactionDateTime" : "#(sTransDateTime)"
}
my featurefile1 : ABC.Feature
Background:
* def envValue = env
* def config = { username: '#(dbUserName)', password: '#(dbPassword)', url: '#(dbJDBCUrl)', driverClassName: "oracle.jdbc.driver.OracleDriver"};
* def dbUtils = Java.type('Common.DbUtils')
* def request1= read(karate.properties['user.dir'] + 'InputRequest.json')
* def endpoint= '/v1/ABC'
* def appDb = new dbUtils(config);
Scenario: ABC call
* configure cookies = null
Given url endpoint
And request request1
When method Post
Then status 200
Feature file from which I am calling ABC.Feature
#tag1
**my featurefile1: XYZ.Feature**
`Background`:
* def envValue = env
Scenario: XYZ call
* def sTransDateTime = function() { var SimpleDateFormat = Java.type('java.text.SimpleDateFormat'); var sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'+00:00'"); return sdf.format(new java.util.Date()); }
* def result = call read(karate.properties['user.dir'] + 'ABC.feature') { sTransDateTime: sTransDateTime }
Problem is,
While executing it, runnerTest has tag1 configured to execute.
Currently, it is ignoring entire ABC.feature to execute and also not generating cucumber report.
If I mention the same tag for ABC.feature (Which is not expected for me as this is just reusable component for me ) then it is being executed but sTransDateTime value is not being passed from XYZ.feature to ABC.feature. Eventually, InputRequest.json should have that value while communicating with the server as a part of the request.
I am using 0.9.4 Karate version. Any help please.
Change to this:
{ sTransDateTime: '#(sTransDateTime)' }
And read this explanation: https://github.com/intuit/karate#call-vs-read
I'm sorry the other part doesn't make sense and shouldn't happen, please follow this process: https://github.com/intuit/karate/wiki/How-to-Submit-an-Issue

Any example to have a java class instance to call once and use in all scenarios in karate with different feature files

I have been using DBUtils class from karate demo, i knew this class was nothing to deal with karate. I have a concern like an example which was given has DBUtlis class is called in background for each and every scenario and it should be mentioned in all featurefiles Background:.
Anything like we configure once and use that DB instance variable in all scenarios?? If yes examples please.
Update after below comment by peter:
config:
Main Feature File:
Reusing DB instance in another feature file
Please confirm whether this is right approach or not?
Dry Run for a String:
var result = karate.callSingle('classpath:featureFiles/dbBackground.feature', config);
config.PersonName = result.name;
Main Feature:
Feature: DB Background
Background:
* def name = "Sandeep";
Other feature:
Feature: Get Account Details
Background:
* def actualname = PersonName;
#golden
Scenario: user 1 details
* def expectedFormat = read('../requestFiles/format.json')
Given url 'https://reqres.in/api/users'
And params ({id: '1'})
When method Get
Then match response.data.email == "george.bluth#reqres.in"
Then print '###################################name is: ', actualname
Then print '###################################name is: ', PersonName
Console result seeing null:
Updated Dry run 2:
Feature: DB Background
Background:
* def name = "Sandeep";
#golden
Scenario: user sample details
* def expectedFormat = read('../requestFiles/format.json')
Given url 'https://reqres.in/api/users'
And params ({id: '1'})
When method Get
Then match response.data.email == "george.bluth#reqres.in"
output:
19:31:33.416 [ForkJoinPool-1-worker-0] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $['data']['email']
19:31:33.416 [ForkJoinPool-1-worker-0] INFO com.intuit.karate - [print] ###################################name is: Sandeep
19:31:33.432 [ForkJoinPool-1-worker-0] INFO com.intuit.karate - [print] ###################################name is: Sandeep
Yes you can initialize it in karate-config.js and then it will be a global variable.
Also look at karate.callSingle(): https://github.com/intuit/karate#hooks

If then else implementation to chose baseURL in Karate DSL

I have a little tricky requirement in Karate. I have a set of baseURL's in my karate.config which are chosen based on the implementation. Here is the snippet of it:
if (env == 'qa') {
config.apiKey = apiKey;
config.tsp_api = 'https://api.qa.tceu.net';
config.svt_dcm = 'https://svt.qa.tceu.net';
config.acn_dcm = 'https://acn.qa.tceu.net';
config.sos_dcm = 'https://sos.qa.tceu.net';
config.cust_dcm = 'https://cust.qa.tceu.net';
Here tsp,svt,acn,sos,cust are some actions.
I have a feature file which passes the action as a parameter:
# Vehicle Initiates the action
When def Perform_Report_Notification = call read('./../common/performActionNotification.feature') { action: '#(action)' }
In the called performActionNotification.feature, I need to pick up the url from the karate.config file based on the action that is passed. For example if the action is sos, then the url should be sos_dcm. If the action is svt then the url should be svt_dcm
Here is the snippet from performActionNotification.feature and what I am currently doing for sos:
Given url sos_dcm
And path '/AU/v1.0/TSP/'+ action
And request RequestPayload
When method post
Then status 200
I want to implement something like an if then else similar to:
if (action == 'sos')
then myurl == 'sos_dcm'
else if (action == 'acn')
then myurl == 'acn_dcm'
else if (action == 'svt')
then myurl == 'svt_dcm'
Given url myurl
And...
And...
...
I tried a sort of a hack and it works but its not a clean way of doing it. Instead of reading the URL from karate.config I am hardcoding it this way:
Given url 'https://'+act+'.qa.tceu.net'
One more thing I tried was
* def myurl = action +'_dcm' #so if action is acn then the variable myurl would be acn_dcm
Given url myurl
...
....
But this hardcodes the url as 'acn_dcm' instead of picking the defined url up from karate.config.
Can someone kindly suggest the best way to implement this?
Here is a hint. JSON is actually a pretty useful data-structure (think hash-map or dictionary) and you can lookup a value without needing an if statement.
* def data =
"""
{
qa: {
sos: 'https://sos.qa.tceu.net',
acn: 'https://acn.qa.tceu.net'
}
}
"""
* def env = 'qa'
* def urls = data[env]
* def action = 'sos'
* def actionUrl = urls[action]
* match actionUrl == 'https://sos.qa.tceu.net'
This should get you on your way :)
EDIT - also see this: https://stackoverflow.com/a/67868935/143475
I've finally used Peter's most elegant solution and it works like a charm!
Here's what I've finally implemented that does not need hardcoding of the endpoint URL's and is driven by endpoints in the karate.config file.
* def data =
"""
{
qa: {
sos: '#(sos_dcm)', # sos_dcm endpoint defined in karate.config file
acn: '#(acn_dcm)',
svt: '#(svt_dcm)'
}
}
"""
* def env = karate.properties['env']; # Driven by maven commandline arg -Denv=qa as an example
* def urls = data[env]
* def action = act # act comes from the calling feature file and has values - sos/acn/svt
* def myUrl = urls[action]
Given url myUrl
...
...
I would suggest looking into using javascript for your conditional logic
So the javascript function takes a param of action and then the if and else statements returns the variable of the url that you need.
Perform the javascript function before you make the request call. and use the variable that is returned by js to determine the logic.
make that js file a common function that can be accessed by multiple feature files.
function determineUrl(action) {
var url = "${urDefaultUrl}";
if (action == "sos") url == "${full url}";
else if (action == "acn") url == "${full url}";
return url;
}
Then in your feature file
* def urlDecider = 'classpath to your js function'
* myUrl = urlDecider(action)
* url myUrl
* Given path ....
For the sake of the community learning, there is one other way I figured out on similar lines of Jawad's solution is using a Java function. They do exactly the same thing as Jawad's solution but just that its a java class doing it. If a project has java class files, then to maintain consistency this solution can be used too.
Here is the how the class file looks:
public class DCMUrlDecider {
static String dcmURL="";
public static String getDCMUrl(String action) {
if (action.matches("sos"))
{
dcmURL = "https://sos.qa.tceu.net";
}
else if (action.matches("acn"))
{
dcmURL = "https://acn.qa.tceu.net";
}
else if (action.matches("svt"))
{
dcmURL = "https://svt.qa.tceu.net";
}
return dcmURL;
}
}
And here is the associated code snippet from the feature file:
* def dcmURLDecider = Java.type('com.TCEU.KarateTests.DCMUrlDecider')
* def myUrl = dcmURLDecider.getDCMUrl(act)
Given url myUrl
....
.....
Once again this means we are still hardcoding URLs in the java class files. Need to learn a way of doing it via karate.config files if possible.

Log requests from called features in reports [duplicate]

We are using the report verbosity option available in Karate
I have a MarketingPreferenceTest.feature calling BBB.feature.
The features are as below:
MarketingPreferenceTest.feature
Background:
* url Url
* table credentials
|Email |Password|
|'aaa#test.com'|'test1234'|
* def result = karate.callSingle('classpath:resources/BBB.feature',credentials)
Scenario Outline: Get MS
Given path 'abc'
When method GET
Then status 200
BBB.feature:
Background:
* configure retry = { count: 5, interval: 1000 }
* configure headers = { 'Content-Type': 'application/json'}
* url authenticationUrl
Scenario: Login
Given path 'login'
And request { email: '#(Email)' , password: '#(Password)' }
And retry until responseStatus == 200 && response.loginResponse.loggedIn == true
When method post
My karate.config has
karate.configure('report', { showLog: true, showAllSteps: false } );
When i run the tests in parallel, i want to see all the Given-When-Then's printed in my cucumber report from BBB.feature. How do i achieve it ?
The cucumber report is shown below which doesn't have step definitions from BBB.feature :
Expected Result: Would like to see the Steps of BBB.feature in my report marked in a rectangle box below
Just make the step involving a callSingle use a Gherkin keyword:
When def result = karate.callSingle('classpath:resources/BBB.feature',credentials)