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
Related
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.
I have a component that displays balances for 4 separate tokens. I'd like to have a function like this:
async updateTokenBalance(token, balance) {
balance = await getTokenBalance(token);
balance = balance.value.uiAmount;
}
which I can call like so:
updateTokenBalance(this.a_acc, this.a_balance);
updateTokenBalance(this.b_acc, this.b_balance);
updateTokenBalance(this.c_acc, this.c_balance);
updateTokenBalance(this.d_acc, this.d_balance);
unfortunately this doesn't seem to work. The only way I've gotten it to work so far is by having 4 separate functions:
async updateATokenBalance() {
let balance = await getTokenBalance(this.a_acc);
this.a_balance = balance.value.uiAmount;
}
// the other 3 analogous
in particular, it's the second line of the function where I try to assign the balance to a data property of this.a_balance that breaks if this.a_balance is passed in as arg.
Is there a way to make it work?
If your data come from an API, you should iterate through the incoming object list and run the function for each item with the analogous arguments.
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.
I've got a helper called feature that looks like this:
hbs.registerHelper('feature', function(request, flag, options) {
if (features(flag, request)) {
return options.fn(this);
} else if (options.inverse) {
return options.inverse(this);
}
});
And used in the template over and over like this:
{{feature request "some-feature"}} ... {{/feature}}
I'd love to be able to remove the request part in the template as it's always the same value and never changes. So I imagine I could bind request to feature when it's rendered, and obviously that changes each time and I don't want it spilling out to other request.
Something like:
res.render("page", {
feature: hbs.helper.feature.bind(null, req)
});
Is this possible?
If you are not using known helpers mode then the helper evaluation will check the context so you can pass in a bind like you have above and it should work.
Under the latest code in handlebars master the eval is something like:
helper = helpers.foo || (depth0 && depth0.foo) || helperMissing
helper.call(depth0, 1, {"name":"foo","hash":{},"data":data}
Where depth0 is the current context object. The caveat here is that helpers are given priority so you need to name them differently. You should also be able to do something like {{./foo bar}} to give priority to the local context's version but it appears that we have a bug where that isn't honored under this particular syntax construct.
I want to unit test the return value of some code which looks similar to this:
Groovy Service code to test:
def findByThisAndThat(something) {
:
def items = []
sql.eachRow(query, criteriaList, {
def item = new Item(name:it.NAME)
items.add(item)
})
[items: items, whatever:whatevervalue]
}
Unit test code plan:
void testFindByThisAndThatReturnsAMapContainingItems(){
Sql.metaClass.eachRow = { String query, List criteria, Closure c ->
// call closure to get passed in items list
// add one new Item(name:"test item") to list
}
def result = service.findByThisAndThat("", "")
assert result.items
assertEquals('test item', result.items[0].name)
}
How can I do that?
Thanks!
A unit test like you're proposing really just tests that given proper data from the database, you assemble it correctly into Item instances. I'd switch instead to an integration test where you have access to a real database and test the whole method with test data in the db.
Unit testing database access typically is more of a test of the mocking code than of your code, so it's often of little use.
Call the closure by using it like a method. Alternatively, you can use Closure.call() as well. Pass in the value for it as the first parameter.
Sql.metaClass.eachRow = { String query, List criteria, Closure c ->
def mockItems = ["test item"]
mockItems.each { item ->
c(item)
// c.call(item) works too
}
}
Note that the Sql metaClass won't get reset at the end of the test. I'd recommend clearing it out after the test:
Sql.metaClass = null