this issue is to provide updates and collect feedback. also will hold some images needed for the wiki
this wiki page will hold details: https://github.com/intuit/karate/wiki/1.0-upgrade-guide

Array.map(), note that karate.map() is still an option
@ptrthomas I had rebuilt the rewrite branch locally and generated karate-2.0.0.jar, and below is test finding:
The same issue also found when I use the karate-2.0.0.jar to run local karate API testing against karate mock server.
@hujunhaorobert thanks for trying out the rewrite branch. I have made changes for bodyPath() to work in the new code: https://github.com/intuit/karate/commit/6ce22ce1668e3939aa23e5b5a8b9b7eb0808019b
I also suspect you may not have switched cleanly to the rewrite branch, there should not be anything under karate-netty anymore, it is all moved to karate-core - anyway please see if the mocks are ok
if you still have trouble, I'll try to get a better way of trying RC releases, but I don't want to release JAR files to maven just yet

passing JS functions top Java code that will be executed on another thread has to be wrapped using karate.toJava()
this may still have issues, in that case - last resort is to move the whole flow to Java
@ptrthomas Yes, I met some trouble to get standalone karate-{version}.jar for my mock testing. In order to regenerate that JAR, I was following the Developer Guide of Build Standalone JAR and ZIP from https://github.com/intuit/karate/wiki/Developer-Guide. I see there is nothing except README.md under karate-netty now, can you specify how to generate Karate standalone JAR, or how can I get a RC release? Thanks!
@hujunhaorobert since all the code is in karate-core it is a little simpler.
karate-core directory in a terminal / consolemvn clean install -P fatjar-DskipTests to the abovekarate-core/target
breaking change - but this was the best way to solve the js threading problem
karate.listen() is gonelisten that just takes a number (expressions also allowed) karate.signal()) use the "magic variable" called listenResult@ptrthomas By following your instruction above, I re-generated the karate-2.0.0.jar successfully, which is good.
I copy this jar file to my mockTest project, and put my customised karate-config.js file under the mockTest project folder, same as main-mock-server.feature. When I launching my mock server by CLI:
java -cp karate-2.0.0.jar com.intuit.karate.Main -m main-mock-server.feature -p {{port-id}}
The program throw an exception when running below line(11) in main-mock-server.feature
Console log is as below:
12:08:41.630 [main] INFO com.intuit.karate.Main - Karate version: 2.0.0
12:08:43.299 [main] ERROR com.intuit.karate - mock-server background failed - main-mock-server.feature:11
com.intuit.karate.KarateException: mock-server background failed - main-mock-server.feature:11
at com.intuit.karate.core.MockHandler.<init>(MockHandler.java:103)
at com.intuit.karate.core.MockServer$Builder.build(MockServer.java:112)
at com.intuit.karate.Main.call(Main.java:221)
at com.intuit.karate.Main.call(Main.java:47)
at picocli.CommandLine.executeUserObject(CommandLine.java:1933)
at picocli.CommandLine.access$1200(CommandLine.java:145)
at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2332)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2326)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2291)
at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2159)
at picocli.CommandLine.execute(CommandLine.java:2058)
at com.intuit.karate.Main.main(Main.java:145)
Caused by: com.intuit.karate.KarateException: >>>> js failed:
01: read('classpath:karate-config.js')
<<<<
org.graalvm.polyglot.PolyglotException: not found: karate-config.js
- com.intuit.karate.resource.ResourceUtils.getResource(ResourceUtils.java:111)
- com.intuit.karate.core.ScenarioFileReader.toResource(ScenarioFileReader.java:122)
- com.intuit.karate.core.ScenarioFileReader.readFileAsStream(ScenarioFileReader.java:97)
- com.intuit.karate.core.ScenarioFileReader.readFileAsString(ScenarioFileReader.java:93)
- com.intuit.karate.core.ScenarioFileReader.readFile(ScenarioFileReader.java:56)
- com.intuit.karate.core.ScenarioEngine.lambda$new$0(ScenarioEngine.java:134)
- <js>.:program(Unnamed:1)
main-mock-server.feature:11
I had a quick debugging, and noticed that there was a bug when parsing the path in getResource(path), here the path=classpath:karate-config.js.
After calling path = removePrefix(path); the path will be a file name(karate-config.js), rather than a real path, which causes throwing the exception. I will leave the bug with you.
@hujunhaorobert it is likely the previous behavior was a bug. in mocks, there is not really a classpath: concept unless you know what classpath is set when you start the JVM. in this case use a relative path or set the directory that karate will use for finding the karate-config.js as per the documentation. if you still feel this is an issue please provide a sample project
Returning object literal with karate.call('some_js_file.js') does not work on the RC.
Example js file:
//SampleJS.js
function fn(){
var A = "2";
var B = "3";
return {
varA:A,
varB:B
}
}
Calling the js file's method using karate.call with below code returns undefined:
var variables = karate.callSingle('SampleJS.js");
console.log(variables.varA);
Calling Java function using obtained variable raises an error.
The following is the sample feature file:
Scenario: Get All Queries
* def query = karate.call('classpath:schema/GetAllQueries.js')
* request { query: '#(query)'}
* method POST
* def queriesName = $response.data.__schema.queryType.fields[*].name
* def query = karate.call('classpath:schema/GetAllMutations.js')
* request { query: '#(query)'}
* method POST
* def mutationsName = $response.data.__schema.mutationType.fields[*].name
* def allQueriesName = karate.append(mutationsName, queriesName)
# allQueriesName = ['[QueryA, QueryB, QueryC, QueryD, MutationA, MutationB, MutationC, MutationD...']
* def Utilities = Java.type('features._GQLCoverageTools.FilterQueries')
* Utilities.printToFile(allQueriesName)
Java file is as below:
package features._GQLCoverageTools;
import com.intuit.karate.graal.JsList;
public class FilterQueries {
public static void printToFile(JsList array) throws Exception{
System.out.println(array);
}
}
The error raised is:
org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (printToFile) on features._GQLCoverageTools.FilterQueries failed due to: Cannot convert '[QueryA, QueryB, QueryC, QueryD, MutationA, MutationB, MutationC, MutationD....'(language: Java, type: com.intuit.karate.graal.JsList) to Java type 'com.intuit.karate.graal.JsList': Unsupported target type.
- <js>.:program(Unnamed:1)
Quite weird considering the data type used is an appropriate one.
@sumargoraymond for your first comment, I cannot replicate: https://github.com/intuit/karate/commit/263ce53140ee1960e27ef655850fcd7b58ead872
and regarding the second, JsList is not intended for end-users, just use normal Java Map / List / primitives like before, please
EDITED:
It works well using List
Hi @ptrthomas , can you try outputting https://github.com/intuit/karate/commit/263ce53140ee1960e27ef655850fcd7b58ead872 with result.varA instead?
Thanks
@sumargoraymond you are absolutely right ! thanks :) should be fixed now

some improvements to the Runner.builder() that are subtle but you are going to enjoy:
System.setProperty()karate.envkarate-config.jskarate.env in parallel-Dkarate.options passed from the terminal does not seem to be honored when using hook on test runner.
E.g. running mvn clean test -Dkarate.options="--tags @test" -Dtest=TestRunner -Dkarate.env=production should run test with the annotation: @test
// TestRunner.java
Results result = Runner.path("classpath:features").hook(new KarateExecutionHook()).tags("~@ignore")
.parallel(10);
class KarateExecutionHook implements RuntimeHook {
// Here we override some of the before and after hooks
}
The test runner will run tests with the default annotation (~@ignore) instead of the @test from the terminal input.
@sumargoraymond this is a tough one. I'm planning to change the way this works. what I propose is:
in fact the code example in the post just above is an example why this is needed. here, karate.env=docker and server.port will be set by Java code and cannot be changed by setting karate.options
that said - if you want to have this flexibility at run time, you have 2 options:
comments from anyone welcome
@sumargoraymond please ignore what I said above, I remembered that frameworks e.g spring / boot give highest priority to command-line, then system properties - and this is the right way to be able to change things at deployment / ci
so I made it work now like it used to, can you please take a look again and confirm
and thanks for trying out the early versions like this - it makes things so much easier 馃憤
Sure thing, will do. Thanks @ptrthomas
@sumargoraymond can you provide the values of the request and response headers, specifically the Cookie and Set-Cookie ones
@ptrthomas Edited: I just tried hitting the same request again and it successfully loaded an xml. Yet on the RC the error is still the same:
io.netty.handler.codec.http.cookie.DefaultCookie.sameSite()Lio/netty/handler/codec/http/cookie/CookieHeaderNames$SameSite;
Cannot seem to find any Cookie nor Set-Cookie on both the request and responses though.
@sumargoraymond unfortunately I can't do anything unless I get some way to replicate, so I'm going to move on to other things. it is possible that your server has a bug and it doesn't support some cookies: https://web.dev/samesite-cookies-explained/
I'm sure you can find a way to replicate. for e.g.
* url 'https://httpbin.org/cookies'
* cookie foo = { value: 'bar', samesite: 'Strict' }
* method get
* status 200
* match response == { cookies : { foo: 'bar', SameSite: 'Strict' } }
* url 'https://widget-pixels.outbrain.com/widget/detect/px.gif?ch=1&rn=1.3815335980445032'
* method get
* status 200
* match responseCookies.akacd_widgets_routing.samesite == 'None'
My bad @ptrthomas , the server does indeed require a certain key value pair for the cookie. After implementing the necessary cookie on the header, it works well on RC.
@sumargoraymond great. I'm still worried about the error message, since it is not helpful and I'm not able to understand how that will happen. please look out for ways you can replicate it - I'm sure you can mask all the values and figure out a way to replicate, thanks
@ptrthomas Just realized it today, here is the set-cookie value from the response header of the failure. Just in case it might be useful:
__cfduid=df9780asomecloudflarecookieid; expires=Wed, 30-Dec-20 09:25:45 GMT; path=/; domain=.ourdomain.com; HttpOnly; SameSite=Lax; Secure
--
@sumargoraymond that helps. I made some config changes, so do test when possible
by the way everything is back on the develop branch, I've updated the developer guide: https://github.com/intuit/karate/wiki/Developer-Guide
Thanks @ptrthomas tests now run like a charm
reading of karate.options from the system properties / command-line is broken in 0.9.9.RC1 - thanks @mihirmehta for the report. this is fixed in the develop branch
If an exception is thrown in karate-config.js, all data about it is swallowed (no stack trace or error message). The only thing shown is:
karate-config.js failed
- com.intuit.karate.core.ScenarioRuntime.evalConfigJs(ScenarioRuntime.java:321)
- com.intuit.karate.core.ScenarioRuntime.beforeRun(ScenarioRuntime.java:299)
Workaround is to catch the exception in JS and log it in JS, though even that doesn't show the whole error.
This happens both when a JS error is manually thrown and when Karate throws an error (like when the file path is wrong in a karate.call statement)
@corp-ray you mean 0.9.9.RC1 right, try now from the develop branch
Yes I meant 0.9.9.RC1, sorry for not clarifying. Okay will test from develop.
On develop, I do now see a better error message. There are some minor things I still notice :
-When karate-config throws, Karate seems to be implying the error comes from the first line of code in each feature file (it prints that line out as an ERROR log)
-If a JS file is called by a JS file, both blocks of JS are printed out in their entirety
-Neither the file name nor the function name of the second JS file are displayed.
-While the line number of the error is now printed, it's not quite easy to see. Looks like this:
org.graalvm.polyglot.PolyglotException: not found: src/test/KarateUtilities/someFile.json
- com.intuit.karate.resource.ResourceUtils.getResource(ResourceUtils.java:114)
- com.intuit.karate.core.ScenarioFileReader.toResource(ScenarioFileReader.java:122)
- com.intuit.karate.core.ScenarioFileReader.readFileAsStream(ScenarioFileReader.java:97)
- com.intuit.karate.core.ScenarioFileReader.readFileAsString(ScenarioFileReader.java:93)
- com.intuit.karate.core.ScenarioFileReader.readFile(ScenarioFileReader.java:52)
- com.intuit.karate.core.ScenarioEngine.lambda$new$0(ScenarioEngine.java:137)
- <js>.:anonymous(Unnamed:20)
The error is on line 20 of the JS file, but because the file name isn't printed here, that might not be clear.
@corp-ray good. this is the best I could do given the fact that we execute the JS in-memory. you are welcome to find ways to improve it further and contribute code. thanks
Hi
vscode debuger doesn't stop when using 0.9.9.RC1
I put together this minimal example, it comes with all required configuration required by vscode karate-runner and two maven profiles one for 0.9.6 and 0.9.9.RC1
Steps:
mvn clean dependency:copy-dependencies -P karate0.9 or mvn clean dependency:copy-dependencies -P karate1.0todos.feature in vscode and place a breackpoint in line 7Karate: Debug button over line 6when running with 0.9.6 dependencies it will stop but it will continue with dep. 0.9.9
debug port seem listening in both cases
cc/ @kirksl
@ivangsa took one look at your pom.xml and it is not in the quick start format. I'll pass on this one, anyone else is welcome to take a look
@ptrthomas fyi I can't seem to find the outputCucumberJson() described here method in the Runner API (neither in the RC on maven central nor in any branch).
I had the following feature file:
Feature: Reusable function
Scenario:
* def uuid = function(){ return java.util.UUID.randomUUID() + '' };
The Graal engine complains of the semi-colon when using this inline syntax.
org.graalvm.polyglot.PolyglotException: SyntaxError: Unnamed:1:54 Expected ) but found ;
(function(){ return java.util.UUID.randomUUID() + '' };)
Removing the semi-colon works and it's something I noticed in the example commit for the Javascript differences but seems like just "code cleanup" in the diff.
Might be good to explicitly call it out in the breaking changes on the Wiki page
_Edit - another minor potential update to the docs:_
I personally use the Java Interop API a lot for several generic functions and utilities for all tests and with the changes on Lists and Maps had to do a lot of updates (no longer receiving a java.util.List but rater com.intuit.karate.graal.JsList or JsMap). Something very simply handled through override but I think it's worth a bullet point in the upgrade page (maybe under here)
@joelpramos I hope you eventually found the outputCucumberJson(true), and I updated the wiki for trailing semi-colons
regarding the JsList and JsMap those should normally never be visible to end-users. but I guess you do Java stuff in a JS block, and are still within a JS block, then this happens. if you have an example or two, I'll add it to the docs, but I'm not inclined to support this. but if you feel it is worth it - it is possible with a little work to make JsList and JsMap implement List and Map, so feel free to take a shot at this - and if it works, go ahead with a PR !
@joelpramos hang on - let me take a look at JsList and JsMap
A few more things that seem to break with the new JS engine.
Scenario:
* def now =
"""
function() {
return java.time.LocalDate.now();
}
"""
* def dateToString =
"""
function(date, format) {
var date_obj;
if(date instanceof java.lang.String) {
if(date.length == 10) {
date_obj = java.time.LocalDate.parse(date);
} else {
if(date.length == 19 && date.charAt(10) == ' ') {
var date_copy = date;
date = date_copy.substring(0, 10) + 'T' + date_copy.substring(11);
}
date_obj = java.time.LocalDateTime.parse(date);
}
} else if(date instanceof java.time.LocalDate) {
date_obj = date;
}
var formatter = (Java.type('java.time.format.DateTimeFormatter')).ofPattern(format);
return formatter.format(date_obj);
}
"""
* def dateToShortString =
"""
function(date, separator) {
return dateToString(date, "yyyy"+separator+"MM"+separator+"dd");
}
"""
The Graal engine doesn't seem to have access to the variable that was previously defined. I'm looking into if it's something easily solved as I'd like to avoid refactoring this as it's a pattern I had our users do quite frequently to make tests look very simple.
Had the following in my karate-config-unit-test.js . Graal now fails cause "config" is not defined. A bad implementation of mine anyways (and I'm not really certain how it worked before) but just flagging in case anyone does the same. Just create the var config = {} as it's a local variable anyways and you return it as part of the javascript function which will then get added to the Karate context.
function fn() {
// config file for the 'unit-test' environment
var env = karate.env; // get java system property 'karate.env'
// sample
if (env == 'unit-test') {
config.env.httpScheme = 'http';
config.env.appUrl = 'localhost';
config.env.appPort = karate.properties['demo.server.port']
if(!config.env.appPort) {
config.env.appPort = 8080
}
}
}
@joelpramos I hope you eventually found the
outputCucumberJson(true), and I updated the wiki for trailing semi-colons
Yes it's in the develop branch, just not in the RC :)
regarding the
JsListandJsMapthose should normally never be visible to end-users. but I guess you do Java stuff in a JS block, and are still within a JS block, then this happens. if you have an example or two, I'll add it to the docs, but I'm not inclined to support this. but if you feel it is worth it - it is possible with a little work to make JsList and JsMap implementListandMap, so feel free to take a shot at this - and if it works, go ahead with a PR !
I actually didn't mind at all with JsList - just overloaded methods. Was just suggesting the note in the documentation. It's not challenging and actually makes it even clearer in the code that method is being used by Karate so it should have a unit test being done from Karate (using a feature file) and the overloaded method can have a unit test directly from Java. I only had 20 or so methods so it was fine, not sure if you'll find people complaining a lot of this change.
@joelpramos I went ahead and did the JsList / Map tweak anyway, I do remember / think that other users are using java-isms in JS: https://github.com/intuit/karate/commit/b122ca8800da99126f6665dcad70e9fb2fae0765
would be good if you can confirm it does work as expected - so things like list.add() should be sufficient instead of list.push()
for the (1) in your example above, pls do try simplify it, I don't get it, I think (2) is fine without any docs
@joelpramos I went ahead and did the JsList / Map tweak anyway, I do remember / think that other users are using java-isms in JS: b122ca8
would be good if you can confirm it does work as expected - so things like
list.add()should be sufficient instead oflist.push()
Just tested - works fine for my changes but the following is not working in "pure" Karate/JS. I assume that's the intended behavior - to align with Graal like specified in the the docs for 1.0.0.
And def list = []
And list.add("test")
for the (1) in your example above, pls do try simplify it, I don't get it, I think (2) is fine without any docs
There are a lot of methods and common functions in testing and too often I've just seen duplication so my preference is to define functions which reuse functions - like any other piece of software. In order to maintain the karate syntax my preference has been to use def and define functions but then within the javascript code itself I want to have access to those Karate variables.
We use it quite frequently even to reuse features that are core or "larger" pieces of tests (e.g. pre-conditions of creating data across several systems which can be reused etc.).
Simpler example which won't work:
* def karateNow =
"""
function() {
return java.time.LocalDate.now();
}
"""
* def tomorrow =
"""
function() {
return karateNow().plusDays(1);
}
"""
The Graal Engine doesn't seem to recognize the function karateNow() which was defined as part of Karate. I think with the older engine there was a concept of Bindings that were always kept up to date after any evaluation of the Karate code / line and this reusability concept worked nicely.
@joelpramos that's weird - this works perfectly for me. in fact the JS engine bindings are kept up-to-date with def - see setVariable(String key, Object value) in ScenarioEngine
* def karateNow =
"""
function() {
return java.time.LocalDate.now();
}
"""
* def tomorrow =
"""
function() {
return karateNow().plusDays(1);
}
"""
* def temp = tomorrow()
* print temp
output:
23:00:30.650 [main] INFO com.intuit.karate - [print] 2020-12-14
In that case it might be something else... I have a .feature file (named _framework-utils.feature_) which is called in my karate-config.js using karate.callSingle() and it's the function inside that framework-utils.feature that is failing to find the reference. Maybe the issue with the bindings could be from the callSingle().
Minimal code:
_karate-config.js_
function fn() {
var config = {};
config.Utils = karate.callSingle('classpath:karate/framework-utils.feature');
return config;
}
_framework-utils.feature_
Scenario:
* def dateToString =
"""
function(date, format) {
// long function to format dates
// posted before but don't think it's relevant for the issue
return "formatted/date/here";
}
"""
* def dateToShortString =
"""
function(date, separator) {
return dateToString(date, "yyyy"+separator+"MM"+separator+"dd");
}
"""
And then on my _karate-utils-js.feature_ unit test I have the following:
Feature: unit test
@dateToShortString-method
Scenario:
When def date = "2020-12-22T03:39:21"
And def dateShortString = Utils.dateToShortString(date, "/")
Then assert dateShortString == '2020/12/22'
And I get the following error below. Note that it's not that the function I'm calling is not found, it's that function can't find the other "reusable" function.
12:40:53.308 [main] ERROR com.intuit.karate - classpath:input/karate/karate-utils-js.feature:32
And def dateShortString = Utils.dateToShortString(date, "/")
>>>> js failed:
01: Utils.dateToShortString(date, "/")
<<<<
org.graalvm.polyglot.PolyglotException: ReferenceError: "dateToString" is not defined
- <js>.:anonymous(Unnamed:2)
Adding the line engine.setVariables((Map)result); (or for me quickly calling with the debugger) in ScenarioBridge.java:226 (after the callOnce is performed) seemed to fix this but I just did as a quick hack. Not sure whether there are any implications to it as I didn't run the unit tests or anything but seems to be the solution.
@joelpramos you mean callSingle() because a LOT of work went into callonce to be able to re-hydrate functions. I am proposing that callSingle() have the limitation that only "pure-data" can be cached. especially since only that would work with the option to cache to file. if you can get callSingle() to work the way you want, great - but it may be tricky. remember callSingle() has to span features
Yes - sorry callSingle(). I'd still like to have the option to have that call done from within the karate-config.js cause it basically allows me to "extend" the Karate DSL.
I understanding the principle you're proposing callSingle() to be for pure-data. At first I tried adding the line as I suggested above but then one of the unit tests failed - the resulting variable had the same values but it was a new / different object.
I ended up extending the ScriptBridge with a new function which is basically a wrapper for the callonce. The same principle as callSingle() but caches the Karate Result and so it avoids parsing that file with every single execution in a Scenario Outline and at the same time retains the functionality I'm looking for.
public Object readAndCallOnce(String filePath) {
String karateReadExp = "read('" + filePath + "')";
Variable variable = this.getEngine().call(true, karateReadExp, false);
return JsValue.fromJava(variable);
}
And in my karate-config.js: config.Utils = karate.readAndCallOnce('classpath:karate/framework-utils.feature');
Let me know if you're good with this and I can open the PR - or if you think it's just best to expose the entire parameters of callonce or have any other suggestions.
Edit
I also changed my "utils" feature to use the prefix in the function call that I assign to the result of the feature. Note the Utils in the return. Not a big deal for me as I see it as the namespace of my additions to the DSL.
* def dateToShortString =
"""
function(date, separator) {
return Utils.dateToString(date, "yyyy"+separator+"MM"+separator+"dd");
}
"""
@joelpramos ok, how about naming that as callonce so that it is just the JS version of the keyword
@ptrthomas is there a new version of the debugger for VS Code? Was having a lot of issues and I now realized that it's the debugger - seems like the background and the scenario execution don't share the same engine / the engine is set to null when I'm using breakpoints.
My current version in VS Code is 0.9.5.
Super weird but I haven't discounted yet that it could be due to my setup using Spring Boot (see discussion here / my retests for others landing on this comment https://github.com/intuit/karate/issues/751#issuecomment-731326676)

When I trigger the scenarios via the Runner API it works fine. I haven't looked too much into the code for the debugger or did a very deep triage of this.
@joelpramos I hope its because there's a second version of karate in deps maybe because you didn't remove karate-apache 0.9.5 or etc.
paging @ivangsa in case some of the recent changes we worked on broke this - refer #1399
would it be possible to replicate ? cc @kirksl
Confirmed I only have karate-junit5. v0.9.5 is the version of the Karate Runner in VS Code but that's the latest in the marketplace. I will try to replicate using standalone JAR version first all will provide some feedback soon.
Hi, this is already happening in version 0.9.9.RC1 but I'll have a look into it... I think I have never used this debug console with karate
Interesting... our team always used in 0.9.5 and it improves productivity to write scripts a lot (especially in large flows for UI testing) as you can try it out, paste in the code, try new command, paste, etc. Same way you can just print variables to see references to triage issues etc.
There were a couple glitches some times but it's like after going back and forth, hot swap etc. multiple times so nothing that we ever really worried about.
I am getting the following error when using the fat jar (either 0.9.9.RC2 or the latest develop branch):
org.graalvm.polyglot.PolyglotException: SyntaxError: No language for id regex found. Supported languages are: [js]
The issue only occurs when I attempt to use a regular expression.
E.g. add the following line to karate-config.js
karate.log('a'.replace(/a/g, 'b'))
@ptrthomas was doing some testing this morning noticed a commit where the relationship between Step and Feature is removed. Seems like a large commit so I'm sure there was a reason to remove it but steps in the "Background" section don't have a scenario associated with so it creates a nullpointer when trying to get the associated feature via the scenario. I've noticed while using the debugger:

Maybe because of Background steps there's a good use case to keep the Feature associated with the Step, not sure the impacts to the changes being implemented (seems like parsing results into a Feature so are you trying to use results to re-execute tests? kind of curious).

@joelpramos thank you for reporting this, indeed my miss. what's new in the code is being able to serialize a full feature-result into JSON and back again, which now gives us a way to split a test suite across different physical machines and aggregate results. I'll fix this right away and this time put in a comment to make it clear
@joelpramos should be fixed now
There is an issue with com.intuit.karate.Main it has the following includes:
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
when trying to run I am getting:
java.lang.ClassCastException: class org.apache.logging.slf4j.Log4jLogger cannot be cast to class ch.qos.logback.classic.Logger
(org.apache.logging.slf4j.Log4jLogger and ch.qos.logback.classic.Logger are in unnamed module of loader 'app')
Just wanted to bring it to your attention. This is observed in com.intuit.karate:karate-core:0.9.9.RC2.
@xbranko works for me. it would help if you can follow this process: https://github.com/intuit/karate/wiki/How-to-Submit-an-Issue
Will do. Sorry.
@ptrthomas you were right. In the simple example all is good. Then I tried to see what is causing the problem, and it turns out it is the slf4j/log4j dependency, in my case brought in by the following line in build.gradle:
implementation 'org.springframework.boot:spring-boot-starter-log4j2'
When running, the following messages appear on the STDERR:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/xx/log4j-slf4j-impl-2.13.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.2.3/yy/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
and the class cast exception is thrown.
If you need/want I can zip the example and share it with you -- just let me know.
I would imagine that many enterprise development projects use slf4j and/or log4j, if not directly, at least transitively, as it is the case for my projects that are all spring based. Would you consider removing logback dependency, or, if not, having two versions of Main, one as it is, and another without logback ? As far as I can tell, the only line that really needs logback is line 205, where the log level is set to WARN.
Thank you for creating such an amazing framework.
@xbranko thanks this is super helpful, just made some changes - if you can use the developer guide to verify in your env that would be great: https://github.com/intuit/karate/wiki/Developer-Guide
@ptrthomas thanks for the fix! It works now (in the develop branch).
In my project we use gradle. Gradle has a notion of implementation dependencies for code, and testImplementation for test dependencies. I used testImplementation to bring karate-core and karate-junit5 and their transitive dependencies. This is all how it should be.
The problem I ran into was that one of my code implementation dependencies transitively brought in a different version of classgraph (io.github.classgraph:classgraph:4.8.69), which was found before the desired one, brought in by karate (io.github.classgraph:classgraph:4.8.93). Once I explicitly added the code implementation dependency to io.github.classgraph:classgraph:4.8.93 it was all good. Don't really like "polluting" build.gradle file with non-direct dependencies -- not sure how to correctly deal with this, just wanted to mention it.
The exception was due to the missing method acceptPaths:
java.lang.NoSuchMethodError: 'io.github.classgraph.ClassGraph io.github.classgraph.ClassGraph.acceptPaths(java.lang.String[])'
at com.intuit.karate.resource.ResourceUtils.<clinit>(ResourceUtils.java:93)
at com.intuit.karate.Runner$Builder.relativeTo(Runner.java:345)
at com.intuit.karate.junit5.Karate.relativeTo(Karate.java:61)
...
@xbranko thanks ! I decided to add classgraph to the list of jars that we maven-shade, I'm guessing that it is quite popular + pervasive, and fortunately it is super-lightweight - so do see if that helps. thanks for the report 馃憤
@ptrthomas first off hats off to you! Many thanks for your agility and willingness to help and promptly fixing the issues!
With the latest karate code (develop branch) my tests are now getting the following exception:
java.lang.NoSuchFieldError: packageAcceptReject
at karate.io.github.classgraph.ClassGraph.acceptPaths(ClassGraph.java:694)
at com.intuit.karate.resource.ResourceUtils.<clinit>(ResourceUtils.java:93)
at com.intuit.karate.Runner$Builder.relativeTo(Runner.java:345)
at com.intuit.karate.junit5.Karate.relativeTo(Karate.java:61)
...
Would it be possible to add sources for karate jars in pre-release maven phase, in addition to release phase? That would be of great help to us, so we can better help you!
@xbranko that is weird, in this case I guess the libs you have in your project means a maven exclude or explicit version force is the only option. I don't know how to add sources - I think it will work only if I do a maven release. if anyone here has any inputs or better ideas, let me know
@xbranko turns out there was a solution for the error you faced. can you try again
Not sure how it worked before (something that Nashorn did in a smart way?) but using stringVariable.bytes no longer works but stringVariable.getBytes() works.
So the following example in the Karate doc:
function fn(creds) {
var temp = creds.username + ':' + creds.password;
var Base64 = Java.type('java.util.Base64');
var encoded = Base64.getEncoder().encodeToString(temp.bytes);
return 'Basic ' + encoded;
}
Should change to:
function fn(creds) {
var temp = creds.username + ':' + creds.password;
var Base64 = Java.type('java.util.Base64');
var encoded = Base64.getEncoder().encodeToString(temp.getBytes());
return 'Basic ' + encoded;
}
Maybe Nashorn had some code to find getters in Java to make it more "javascript like".
@joelpramos yes I'd noted this as well: https://github.com/intuit/karate/wiki/1.0-upgrade-guide#javabean-property-short-cuts-dont-work-in-all-cases
what I think is happening is within the round brackets, none of the java-bean magic can happen.
BTW I'm going offline, but I had a late thought that instead of expecting back-ticks for that PR, it may be enough to check for the ${} pattern or / even just add the back-ticks from the code. do think it over thx.
Good, missed that from the guide. Yeah can add the backticks automatically if the ${anything} exists, makes it smoother
@joelpramos thank you for reporting this, indeed my miss. what's new in the code is being able to serialize a full feature-result into JSON and back again, which now gives us a way to split a test suite across different physical machines and aggregate results. I'll fix this right away and this time put in a comment to make it clear

Still an issue when I forced an error in the karate-config.js - the debug info tries to write the error based on the first line of the background which fails because of the nullpointer on the scenario. Should be this.getFeature() . I'm also going offline but can fix it in my PR tomorrow or during the week when I made that tweak for the backticks.
@joelpramos thanks ! fixed now
@xbranko turns out there was a solution for the error you faced. can you try again
Confirming all is as expected and working fine in the latest develop branch. Many thanks for fixing this!
I don't know how to add sources - I think it will work only if I do a maven release. if anyone here has any inputs or better ideas, let me know
Trying to answer my own question, I just tried, If the following plugin directive is added to the build block (lines 60-82 in the karate-parent/pom.xml file), the *-sources.jar are generated and appear in the local maven repository:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
This is copy of the lines 89-101 in the karate-parent/pom.xml file. If this is acceptable, perhaps those lines can be _moved_ there, to avoid duplication and confusion.
@xbranko okay, see how it works now, thx
@xbranko okay, see how it works now, thx
Confirming all is good. Source jars getting created in local maven repository. Thanks for adding this!
In the ScenarioFileReader class when reading a file that has the prefix _"file:"_ it seems like it's using the relative path where Karate is executing. Was it intentional? Seems like the ability of using absolute paths is gone with line 125 of ResourceUtils (2nd screenshot).


I've just tried running standalone karate-core jar from my local maven cache, and it is failing.
java -jar <path_to_jar>/karate-core-2.0.0.jar -h
produces:
Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
at com.intuit.karate.FileUtils.<clinit>(FileUtils.java:48)
at com.intuit.karate.Main.main(Main.java:220)
Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
... 2 more
Few more jars need to be added to it. slf4j-api-xx.yy.zz.jar that contains org/slf4j/LoggerFactory and most likely some other jars that get pulled transitively when running in an IDE, where all is good.
Perhaps a smoke test or two could be added to run in the post install phase to verify that the newly created jar is OK.
@xbranko I think that is fine. use the "fatjar" and you won't have this problem. the fatjar is now around 50 MB. see the dev guide for build instructions: https://github.com/intuit/karate/wiki/Developer-Guide#build-standalone-karate-robot-jar
@joelpramos the workingDir is only used for formatting the path, so if you gave file:/foo it should use /foo as expected. I think it is fine, see if you can write a test-case to fail it
@joelpramos the
workingDiris only used for formatting the path, so if you gavefile:/fooit should use/fooas expected. I think it is fine, see if you can write a test-case to fail it
Yeah ignore me... I probably didn't save my file or something when I was originally testing that change and was using the path _without_ the 'file:'. The behavior is correct.
In previous version 0.9.6 karate was resilient ignoring params set with a null value, in latest 0.9.9.RC2 this throws an error
We would like to keep relying on previous behavior as it feels more resilient.
The reason is we generate parametrizable features from OpenAPI definitions, that then are called from different tests with different parameters... Relying on this, caller can forget about setting non-required parameters.
This illustrates an simple scenario that works on 0.9.6 and fails on 0.9.9.RC2
Scenario:
* url 'https://jsonplaceholder.typicode.com/todos'
* def arg = { params: { someparam: 'value'}}
Given path '', 1
And param nullparam = arg.params.nullparam
And param someparam = arg.params.someparam
And param arrayparam = [#(arg.params.someparam), #(arg.params.nullparam)]
When method get
Then status 200
* match response contains { id: '#number', title: '#string' }
I have a PR for this.
@ptrthomas before I go down a rabbit hole let me know if this is a limitation of the Graal engine you mention in the upgrade guide.
When calling a feature in the background (and not assigning a it to a variable) that reads a .json file with keys, those json key variables are not accessible in the caller context nor in any features called from there (obviously if not accessible in the caller already).

_js-read.feature_

_js-read-reuse.feature_

_js-read-3.js_

_js-read-called.feature_
Something I noticed is that a Feature file with only Background section will not have those background steps executed. The background steps are included with each ScenarioRuntime and since there's no Scenario parsed and so the background steps are not executed. Might be worth a note in the upgrade guide - I see I had a few features just with Background sections mostly to reuse and init parameters.
After putting those steps into a Scenario it still didn't work so my gut feeling is because of that limitation of Graal.
@joelpramos can you check if karate.set('varname', value) suffices as a workaround, in that case I'll leave it as is. it may actually be a good thing, local hiding of variables. for e.g. this particular diff: https://github.com/intuit/karate/wiki/1.0-upgrade-guide#json-variables-no-longer-mutable-by-called-features
Yeah agree could be a good thing. Seems like we if assign the result of the called feature to a variable (see below assigned to varInContext) that we have access to the variables within that feature.
Feature:
Background:
Scenario:
* call read 'js-read-3.json'
* def storedVar = call read 'js-read-3.json'
#* karate.set('storedVar', storedVar)
Feature:
Background:
* def varInContext = callonce read('this:js-read-reuse.feature')
* call read('this:js-read-reuse.feature')
Scenario: calling feature that will read a json file and then call a feature that uses data from that json
* match varInContext.storedVar.thirderror[0].id == 1
* match varInContext.thirderror[0].id == 1
* match storedVar.thirderror[0].id == 1
* match thirderror[0].id == 1
* def result = call read('js-read-called.feature@name=checkReadingThirdJsonKeys')
3rd matching condition (accessing storedVar) fails. Trying to use the Karate API directly produces the same result ( * karate.call(true, 'this:js-read-reuse.feature') ).
I'll give it some thought as on one hand I agree hiding variables could be good but maybe the Karate API could force that shared scope concept that existed before.
@ptrthomas the above works and it's how we use call read to call the reusable feature.
Will make scope shared:
* call read('this:js-read-reuse.feature')
Will not make scope shared:
* call read 'this:js-read-reuse.feature'
I think you have some documentation on this somewhere already, it rings as the space and correlation with shared scope rings a bell.
There's still some issue in my own tests that I can't seem to reproduce with a unit test but it must related to this. I'll check in these unit tests regardless cause I ended up doing a bunch to try to reproduce. Still trying to find some breaking issues and this one regardless doesn't seem to be worth my time to investigate (probably something minor that can easily be refactored).
@joelpramos see last row of table: https://github.com/intuit/karate#call-vs-read
replace does not seem to work correctly anymore. After replacing a string, the toString() method still returns the raw string. Consider this test:
Scenario:
* def text = "words that need to be {replaced}"
* replace text.{replaced} = "correct"
* match text == "words that need to be correct"
* match text.toString() == "words that need to be correct"
Both matches should be equivalent, and in 0.9.6 they both work correctly. But in 0.9.9.RC2 the second match fails with the following error message:
$ | not equal (STRING:STRING)
'words that need to be {replaced}'
'words that need to be correct'
@Lorkenpeist excellent catch thank you ! fixed in develop. we should have an RC3 in a day or 2 but you can use the developer guide (see wiki) to verify.
all: 0.9.9.RC3 has been released: https://twitter.com/KarateDSL/status/1349603552478461953
@Lorkenpeist excellent catch thank you ! fixed in
develop. we should have an RC3 in a day or 2 but you can use the developer guide (see wiki) to verify.
Looks good in 0.9.9.RC3, thanks!
Finally figured out the issue @ptrthomas - it's not with the read. When using a dynamic variable in the Examples table we loose access to any variable or context defined in the background. There's probably some re-init of the engine somewhere?
Below scenario no 1 works but scenario no 2 fails with the following:
20:11:48.722 [main] ERROR com.intuit.karate - classpath:com/intuit/karate/core/js-read-2.feature:20
* match data.error[__num].id == __row.id
>>>> js failed:
01: data
<<<<
org.graalvm.polyglot.PolyglotException: ReferenceError: "data" is not defined
- <js>.:program(Unnamed:1)
Feature:
Background:
* def data = call read 'js-read.json'
* def test_data = data.error
Scenario Outline: using a scenario outline, try to access background
* match data.error[0].id == value
* match test_data == "#present"
Examples:
| key! | value! |
| 0 | 1 |
#| 1 | 2 |
Scenario Outline: using a scenario outline with dynamic variable as data, try to access background variables
* print __row
* match data.error[__num].id == __row.id
* match error[0].id == 1
Examples:
| test_data |
While doing it I also noticed something that I think is another error but I can't recall whether I used it before:
Feature:
Background:
* def data = call read 'js-read.json'
* def test_data = data.error
Scenario Outline: using a scenario outline, try to access background
* match data.error[key].id == value
* match test_data == "#present"
Examples:
| key! | value! |
| 0 | 1 |
| 1 | 2 |
Following error:
20:14:21.516 [main] ERROR com.intuit.karate - classpath:com/intuit/karate/core/js-read-2.feature:8
* match data.error[key].id == value
Could not parse token starting at position 7. Expected ?, ', 0-9, *
It triggers when I try to use a variable as the array index - data.error[key].id == value . Seems weird as position 7 is the match and so it might be some regex that is broken.
I'm logging off now so won't be picking it up immediately. I also noticed some weird behaviors with the driver.input() when we pass something like this:
`
* def credentials =
"""
{
"username" : '#(user)',
"password" : '#(pass)'
}
... some code here, call a reusable feature with the following
* input(loginScreen.username, credentials.username)
"""
`
In which the user variable is null (in my case was due to background issue reported above) I get me the following error:
*** step failed: >>>> js failed:
01: input(loginScreen.username, credentials.username)
<<<<
org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (input) on com.intuit.karate.driver.chrome.Chrome@5aadeb88 failed due to: Multiple applicable overloads found for method name input (candidates: [Method[public com.intuit.karate.driver.Element com.intuit.karate.driver.DevToolsDriver.input(java.lang.String,java.lang.String)], Method[public default com.intuit.karate.driver.Element com.intuit.karate.driver.Driver.input(java.lang.String,java.lang.String[])]], arguments: [input[name='Login-LoginScreen-LoginDV-username'] (String), DynamicObject<undefined>@2c72c81e (Nullish)])
- <js>.:anonymous(Unnamed:1)
Super edge and only happens to me because of the previous issue but it's cause the second value is null. Might be something worth trying to fail more gracefully if possible.
@joelpramos as I linked earlier, please note that call read 'js-read.json' may not be what you intended.
for the second problem please see: https://github.com/intuit/karate/issues/1280
can you extend my simple example below to replicate:
Feature:
Background:
* def data = [{ name: 'one' }, { name: 'two' }]
Scenario Outline:
* print name
Examples:
| data |
Context to "anotherVariable" is lost but context of "data" is accessible.
Feature:
Background:
* def anotherVariable = 'hello'
* def data = [{ name: 'one' }, { name: 'two' }]
Scenario Outline:
* print name
* print '> ' + anotherVariable
Examples:
| data |
14:52:14.135 [main] ERROR com.intuit.karate - classpath:com/intuit/karate/core/js-read-2.feature:14
* print '> ' + anotherVariable
>>>> js failed:
01: karate.log('[print]','> ' + anotherVariable)
<<<<
org.graalvm.polyglot.PolyglotException: ReferenceError: "anotherVariable" is not defined
- <js>.:program(Unnamed:1)
Context to "anotherVariable" is lost but context of "data" is accessible.
Feature: Background: * def anotherVariable = 'hello' * def data = [{ name: 'one' }, { name: 'two' }] Scenario Outline: * print name * print '> ' + anotherVariable Examples: | data |14:52:14.135 [main] ERROR com.intuit.karate - classpath:com/intuit/karate/core/js-read-2.feature:14 * print '> ' + anotherVariable >>>> js failed: 01: karate.log('[print]','> ' + anotherVariable) <<<< org.graalvm.polyglot.PolyglotException: ReferenceError: "anotherVariable" is not defined - <js>.:program(Unnamed:1)
This line in ScenarioRuntime.java solves it but I'm not sure if it's the best place as I find it odd in only happens when the expression in the Examples table is dynamic. If you think this is good enough I can open the PR.

Simplified unit test (first one fails with current code base, both pass with the change above):
Feature:
Background:
* def anotherVariable = 'hello'
* def data = [{ name: 'one' }, { name: 'two' }]
Scenario Outline:
* match name == "#present"
* match anotherVariable == "hello"
Examples:
| data |
Scenario Outline:
* match name == "#present"
* match anotherVariable == "hello"
Examples:
| name |
| test |
I ended up writing a bunch of unit tests for combinations of reading jsons with space and without space etc so I'll check them all in once we sort this one out.
@joelpramos thanks ! this is the solution I would go with, do submit PR
@joelpramos never mind, I made the change and actually it was not as straightforward as I expected, see second commit attempt above
I think now the __arg variable is only accessible in the Background section (using my test scripts) but it's null in the Scenario section itself. Seems to happen only when there's this dynamic expression. Let me try to create a minimal unit test to reproduce - I'll check them in a PR as I ended up creating a lot of tests to try to reproduce this.
Btw even reviewing your PR what I still didn't gather was why this wasn't a problem if the Examples table was fixed as based on my understanding the code flow is about the same.
PS: should we create a new issue to continue this discussion outside of the release thread?
Getting JSON size based on the provided example https://github.com/intuit/karate/blob/master/karate-junit4/src/test/java/com/intuit/karate/junit4/demos/js-arrays.feature no longer work on latest RC:
* def payload = { a: 1, b: 2 }
* def size = function(o){ return o.size() }
* def length = size(payload)
It raises error:
>>>> js failed:
01: size(payload)
<<<<
org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (size) on {a=1, b=2} failed due to: Unknown identifier: size
- <js>.:anonymous(Unnamed:1)
@sumargoraymond yes, thanks - this is expected: https://github.com/intuit/karate/wiki/1.0-upgrade-guide#java-api-s-for-maps-and-lists-are-no-longer-visible-within-js-blocks
Found an issue when matching using regex:
* def foo = 'organization secret'
* match foo == '#regex (?i).*organization.*'
Regex was working on 0.9.6 and validated through https://regex101.com/
It raises an error on the latest RC:
Unclosed group near index 1
(
@sumargoraymond I have no idea about this. I hope someone here can volunteer to investigate and fix that if it is an issue. note that Java rules are used: https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html
in addition to above comment I just realized that this may have something to do with the issue raised by @jkeys089 in a comment above and also refer #1407 - all: do weigh in with your thoughts. I had a quick look and couldn't get it working
Thanks @ptrthomas, will do look at it
Trying to match array using contains any will invoke an error on the following scenario:
* def data = { foo: [1, 2, 3] }
* match data.foo contains any [1, 2, 3, 4]
It works on 0.9.6 so I assume it originally serves to check whether the array of data.foo contains any of the subset of [1, 2, 3, 4]
On the latest RC it raises an error complaining the length is less than expected (Comparing with just contains instead of contains any)
$ | actual does not contain expected | actual array length is less than expected - 3:4 (LIST:LIST)
[1,2,3]
[1,2,3,4]
Notes: Using the following work for the above scenario:
* def allowed = ['Music', 'Entertainment', 'Documentaries', 'Family']
* def actual = ['Entertainment', 'Family']
* match each actual == '#? allowed.includes(_)'
@sumargoraymond thanks once again, the contains any is fixed in develop
We are getting a lot of "org.graalvm.polyglot.PolyglotException: Java heap space" when debugging or printing large objects (like scenario results) but also when calling scenarios from a js for/while loop:
Before running some tests we have a "crawler" that searches for test-data in a master/detail way until we get enough data of each category, that's why we use a js loop so we can breakas soon as we get enough data...
Do you know/recommend any strategy this?
(I'll add a PR for the debugging part using JsonUtils.toJsonSafe instead of toJson)
@ivangsa does this answer your question ? https://twitter.com/KarateDSL/status/1305144489523048448
@ptrthomas yess, we knew this but didn't make the association yet.. thank you!
@ivangsa awesome. also the same trick works for the call read('some.feature') fun where fun can be a function, so this is not restricted to scenario outlines !
to everyone listening - there are SO many changes that I'm not sure if I can add all of those to the readme :(
I'm seeing an undesirable warning when my feature calls another feature which happens to use the same variable name. I have 2 feature files:
myFeature.feature
Feature:
Scenario:
* def myVar = "some value"
* match myVar == "some value"
* def otherVar = call read("getOtherVar.feature")
* match otherVar.value == "some other value"
# should not be overwritten by the value defined in getOtherVar
* match myVar == "some value"
getOtherVar.feature
Feature:
Scenario:
* def myVar = "some temporary value"
* match myVar == "some temporary value"
* def value = "some other value"
Functionally, this works as expected. When getOtherVar is called, it defines its own value for myVar in its own context, so the value in myFeature's context is not overwritten. However, I am seeing this warning which does not appear in 0.9.6:
15:23:14.855 [pool-1-thread-2] WARN com.intuit.karate - over-writing existing variable 'myVar' with new value: "some temporary value"
This warning was troubling at first as it made me suspect that a variable could be accidentally overwritten if a called feature happens to use the same variable name. Fortunately it turns out that is not the case, but I do not believe I should be seeing this warning.
@Lorkenpeist I'm going to keep it as it is - but you are welcome to submit a PR. any variable in a calling feature "exists" in called features no matter if it is "shared" or "isolated".