here is a similar code that im usein
Feature: Create a company instance
Background: Creating the company instance in background
* url baseUrl
* def login = call read('classpath:blackbook/common/getToken.feature')
* def newCI = call read('../endpoints/create_companyinstance.feature')
* def id = newCI.response.data.id
* configure afterScenario =
"""
function(){
var ciID = karate.get('id');
console.log(ciID);
}
"""
After that i will run it. and the login call and newCI call runs fine but then i will get the following error
[ERROR] Scenario: Doing a get call and then some verifications Time
elapsed: 0.005 s <<< ERROR!
java.lang.RuntimeException: unexpected 'configure' key:
'afterScenario'
Please forgive noobness.
Jawad - apologies as I am responsible for prematurely adding documentation for un-released features.
"After hooks" is available in 0.7.0.RC2 - it would be great if you can try that and confirm if it looks good. Even though it is a "release candidate" - as the dev of Karate, I can confirm it should be good for use - and you will not have any breaking changes when "final" arrives (soon).
Related
Getting connection reset error all of a sudden. When running karate tests. Was running perfectly fine before.
01: new tokenGenerator().generateToken(appIdSC,secretSC,tokenUrl,accountsScope,'POST')
<<<<
org.graalvm.polyglot.PolyglotException: Connection reset
java.net.SocketInputStream.read(SocketInputStream.java:210)
java.net.SocketInputStream.read(SocketInputStream.java:141)
sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
Here is how the java class is being read in feature file to get token:
Background:
def tokenGenerator = Java.type('Utility.JwtTokenGenerator')
def authHeader = new tokenGenerator().generateToken(appIdSC,secretSC,tokenUrl,accountsScope,'POST')
Background:
def tokenGenerator = Java.type('Utility.JwtTokenGenerator')
def authHeader = new tokenGenerator().generateToken(appIdSC,secretSC,tokenUrl,accountsScope,'POST')
Expected it to run as usual
I'm trying to create a dynamic request that should receive 2 parameters (username and password). To do so, I defined the below JSON file:
{
"grant_type": "password",
"client_id": "myClient",
"username": "#(username)",
"password": "#(password)"
}
To call my JSON, I set the below feature file where my intention is to define a variable for each parameter i want to send
Scenario: Get Sugar access token
Given url baseUrl
And path 'oauth2/token'
And def username = 'user'
And def password = 'password'
And def requestBody = read('classpath:jsonFiles/requests/authRequest.json')
When request requestBody
And method POST
Then status 200
* print 'Response: ', response
Unfortunately, when I run the scenario, i'm getting below error message
10:59:59.111 [main] INFO com.intuit.karate - Karate version: 1.4.0-SNAPSHOT
10:59:59.286 [main] INFO com.intuit.karate.Suite - backed up existing 'target\karate-reports' dir to: target\karate-reports_1672160399285
10:59:59.303 [main] INFO c.intuit.karate.core.FeatureRuntime - found scenario at line: 4
11:00:00.174 [main] ERROR com.intuit.karate - src/test/java/myCompany/testautomation/features/auth/getToken.feature:11
And def requestBody = read('classpath:jsonFiles/requests/authRequest.json')
js failed:
>>>>
01: read('classpath:jsonFiles/requests/authRequest.json')
<<<<
org.graalvm.polyglot.PolyglotException: not found: jsonFiles/requests/seugarAuth/authRequest.json
- com.intuit.karate.resource.ResourceUtils.getResource(ResourceUtils.java:126)
- com.intuit.karate.core.ScenarioFileReader.toResource(ScenarioFileReader.java:129)
- com.intuit.karate.core.ScenarioFileReader.readFileAsStream(ScenarioFileReader.java:99)
- com.intuit.karate.core.ScenarioFileReader.readFileAsString(ScenarioFileReader.java:95)
- com.intuit.karate.core.ScenarioFileReader.readFile(ScenarioFileReader.java:54)
- com.intuit.karate.core.ScenarioEngine.lambda$new$0(ScenarioEngine.java:120)
- <js>.:program(Unnamed:1)
src/test/java/myCompany/testautomation/features/auth/getToken.feature:11
The use of classpath: makes more sense for Java projects. It looks like you are using the Visual Studio code extension, in which case your workspace root folder will be the classpath unless to switch to "Maven mode" or equivalent: https://marketplace.visualstudio.com/items?itemName=karatelabs.karate#run-mode
Or another option is to use a Java "runner" and use the Java extension for VS Code.
If all that above doesn't make sense, for now just keep things simple and use relative paths. For example if authRequest.json is side-by-side with your feature file, this would work:
* def requestBody = read('authRequest.json')
Relative paths and the this: prefix can be used, but I leave that to you to research: https://github.com/karatelabs/karate#path-prefixes
I have a main feature file where I have included a "setup" feature file that should add some test data. This setup feature file has an annotation that I have called #ignore. However, following the instructions in this Can't be enable to #ignore annotation for the features SO answer, but I am still seeing the setup feature file being run outside of the main test feature.
Main feature file, unsubscribe_user.feature:
Feature: Unsubscribe User
Background:
* def props = read('properties/user-properties.json')
* url urlBase
* configure headers = props.headers
* def authoriZation = call read('classpath:basic-auth.js') { username: 'admin', password: 'admin' }
* def testDataSetup = call read('classpath:com/meanwhileinhell/app/karate/feature/mockserver/testDataSetup.feature') { data1: #(props.data1), data2: #(props.data2) }
Scenario: Unsubscribe user
...
...
Scenario: Remove test data
* def testDataTearDown = call read('classpath:com/meanwhileinhell/app/karate/feature/mockserver/testDataTearDown.feature') { data1: #(props.data1), data2: #(props.data2) }
...
testDataSetup.feature file
#ignore
Feature: Add data to REST Mock Server
Background:
* url mockServerUrlBase
Scenario: Add data
* print 'Adding test data'
Given path 'mapping'
And request { data1: '#(data1)', data2: '#(data2)' }
When method post
Then status 201
Now from my Java runner class, I have added #KarateOptions(tags = "~#ignore").
import org.junit.runner.RunWith;
import com.intuit.karate.KarateOptions;
import com.intuit.karate.junit4.Karate;
import cucumber.api.CucumberOptions;
#RunWith(Karate.class)
#CucumberOptions(features = "classpath:com/meanwhileinhell/app/karate/feature/unsubscribe_user.feature")
#KarateOptions(tags = "~#ignore")
public class KarateTestUnSubscribeUserRunner {
}
However, I can still see my print statement in my setup class being called, and two POSTs being performed. I have also tried running my suite with the following cmd options, but again, still see the feature file run twice.
./gradlew clean test -Dkarate.env=local -Dkarate.options="--tags ~#ignore" --debug
I am following this wrong somewhere? Is there something I can add to my karate-config.js file? I am using Karate version 0.9.0.
Annotations only work on the "top level" feature. Not on "called" features.
If your problem is that the features are being run even when not expected, you must be missing something, or some Java class is running without knowing it. So please follow this process and we can fix it: https://github.com/intuit/karate/wiki/How-to-Submit-an-Issue
EDIT: I think I got it - please don't mix CucumberOptions, in fact we deprecated it, use only KarateOptions. Even that is not recommended in 0.9.5 onwards and you should move to JUnit 5.
Read the docs: https://github.com/intuit/karate#karate-options
I have this test that launches a service at port 7000 and inside the only endpoint I do a failing assertion:
#Test
fun `javalin assertion should fail`() {
Javalin.create()
.get("/") { assertTrue(false) }
.start()
newHttpClient().send(
HttpRequest.newBuilder()
.uri(URI.create("http://localhost:7000/"))
.GET().build(),
discarding()
)
}
The problem is that the test always passes (but it should fail):
(same behavior happens by running ./gradlew test)
... even though there's a console output claiming that a test failed:
[Test worker] INFO io.javalin.Javalin - Listening on http://localhost:7000/
[Test worker] INFO io.javalin.Javalin - Javalin started in 356ms \o/
[qtp2100106358-22] ERROR io.javalin.Javalin - Exception occurred while servicing http-request
org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
..
Probably, it's being run in another thread, but I wonder if there's a way to attach it to the same context.
(Weirdly, in another scenario in my app - that I couldn't isolate - it properly fails.)
TLDR
To make the test fail as you'd expect, add an assertion on the response you get from your Javalin instance.
#Test
fun `javalin assertion should fail`() {
Javalin.create()
.get("/") { assertTrue(false) } // or any expression that throws an Exception, like Kotlin's TODO()
.start()
val javalinResponse: HttpResponse<Void> = newHttpClient().send(
HttpRequest.newBuilder()
.uri(URI.create("http://localhost:7000/"))
.GET().build(),
discarding()
)
assertThat(javalinResponse.statusCode()).isEqualTo(200) // will fail with Expected: 200, Actual: 500
}
Details
There're two distinct steps in the test: new Javalin instance configuration + build, and calling that instance with a HttpClient.
In Javalin configuration + build step it gets instructed to do assertTrue(false) when called on the / endpoint. assertTrue(false) will throw an AssertionFailedError, but the behavior will be the same if you throw something else there. Now, as many (all?) other webservers, Javalin / Jetty will try to catch any uncaught exceptions that happen within it and return a HTTP response with code 500 (Internal Server Error).
Indeed, this all happens in another thread, as internally a Jetty webserver instance is being launched, which does the port listening, HTTP request / response handling and other important stuff.
So when later in the test an HTTP call is performed to the new Javalin instance, it gets the 500 (Internal Server Error) response successfully, and as originally there are no assertions on the response and there were no uncaught exceptions, the test is deemed successful.
Never do assertions inside the Javalin handler, because if the test fails, the JUnit exception is swallowed by Javalin and the test fails silently (better explained in the other answer). The solution is to make an assertion outside, in the end, as in the Arrange, Act, Assert pattern.
How? You store what you want to assert inside the handler and assert it later. For example, if it's a POST.
var postedBody: String? = null
fakeProfileApi = Javalin.create().post("profile") {
postedBody = it.body()
}.start(1234)
val profileGateway = ProfileGateway(apiUrl = "http://localhost:1234")
profileGateway.saveProfile( // contains the HTTP POST
Profile(id = "abc", email = "john.doe#gmail.com".toEmail())
)
JSONAssert.assertEquals(
""" { "id": "abc", "email": "johndoe#gmail.com" } """,
postedBody, true
)
If it's a GET, it's easier:
fakeProfileApi = Javalin.create().get("profile/abc") {
it.result(""" {"id": "abc", "email": "johndoe#gmail.com"} """)
}.start(1234)
val profileGateway = ProfileGateway(apiUrl = "http://localhost:1234")
val result = profileGateway.fetchProfile("abc") // contains the HTTP GET
assertEquals(
Profile(id = "abc", email = "john.doe#gmail.com".toEmail()),
result
)
More info: Unit testing a gateway with Javalin
I am trying to make a simplified version of test report where I am generating a single HTML file report containing only assertion and error response message when there is any (attempting to not publish all the logs and steps).
I understand that we have hooks in karate. However I have looked for karate objects in the github but unable to found any objects where I can extract the response from (to be passed to the js function called on hook)
What I am doing right now is this:
Config:
//karate-config.js
karate.configure('afterScenario', karate.call('classpath:hooks.js'));
Hook:
//hooks.js
//Looking on how to extract the response and log it here
function(){
var info = karate.tags;
karate.log('Tags', info);
}
Am I missing anything on the karate objects? Or this should be achieved in another way?
Thanks a lot!
Try this:
var response = karate.get('response');
EDIT better example:
Background:
* configure afterScenario = function(){ karate.log('***', karate.get("response.headers['X-Karate']")) }
Scenario:
Given url 'http://httpbin.org'
And path 'headers'
And header X-Karate = 'test'
When method get
# this will fail
Then status 400
I have tried with both karate.get('response') and response directly, and both work. If you use karate.call() pass the response as a parameter.