Open flowt-au opened 1 year ago
Anyone there??? Can someone just let me know if this works or not? If not, how to workaround? Thanks, Murray
@flowt-au stumbled across this myself. Quickly looking at extension sources, I have a strong suspicion @BeforeAll
/@AfterAll
are not supported at the moment.
I will look during the weekend on how can we get it working, but no quick solution - even if I am able to get it, then PR needs to be approved, version incremented, and an update published for the extension. Hope extension authors could help me with this.
@abuinitski Thank you! Sorry for my terse previous message. I was getting frustrated fighting with my flakey tests! Apologies.
What I am looking for is two things (I think):
CucumberQuarkusTest
might work?Thanks again. It is great to be able to use Cucumber with Quarkus.
Regards, Murray
As these are Cucumber annotations instead of adding this directly to the CucumberQuarkusTest
TestRunner
you should be adding those @BeforeAll/@AfterAll
into a separate class living in the same package as your steps.
By default Cucumber should load all @BeforeAll/@AfterAll
methods that are placed in a class in the same package as the TestRunner
itself.
You can also put those classes into another package but then you need to declare the package as extra glue in the CucumberOptions
.
@flowt-au I am not a contributor to this extension, just having similar problems to yours.
@After
hook, and inspecting context to understand that "this was the last scenario in a feature", but I don't immediately see how it's doable with cucumber-jvm (did this in the past with cucumber for node).@christophd: Thanks for your explanation which helped me understand the "scope". I am fairly new to Java and still have much to learn.
The reason I asked my question in the first place was even though I tried placing the @BeforeAll in one of my step files, it did not run. And, even after better understanding the scope, I still cannot make it work.
Here is a screenshot of my folder structure:
My TestRunner.java
file:
package org.flowt.shopping.services;
import io.quarkiverse.cucumber.CucumberOptions;
import io.quarkiverse.cucumber.CucumberQuarkusTest;
import io.quarkus.test.junit.main.QuarkusMainTest;
@QuarkusMainTest
@CucumberOptions(
features = { "src/test/resources" },
plugin = { "pretty", "html:target/cucumber-reports/report.html" }
)
public class TestRunner extends CucumberQuarkusTest {}
and the TestRunnerGlobal.java
:
package org.flowt.shopping.services;
import io.cucumber.java.BeforeAll;
import io.quarkus.logging.Log;
public class TestRunnerGlobal {
@BeforeAll
public static void beforeall() {
Log.info("============= BEFORE ALL ==============");
}
}
My tests all pass. When I look at the Terminal log the "============= BEFORE ALL =============="
is not present. (I have removed all tests except one to ensure the Terminal output is not being truncated).
If I place the @BeforeAll
in one of my running step definition files, it does not run there either.
Possibly related to this "scoping" issue is that I am also using a @ParameterType
annotation which works but throws exceptions if I place it in the TestRunnerGlobal.java
file.
So, here is the annotation:
@ParameterType(value = "true|True|TRUE|false|False|FALSE")
public Boolean booleanValue(String value) {
return Boolean.valueOf(value);
}
It works if it is in one of my step definition files. It can be referenced from other step files. eg:
@Then("I should get a SimpleList packet with success={booleanValue}, size={int}, data={string}, failMsg={string}") // etc
Now, if I MOVE the ParameterType to the TestRunnerGlobal.java
file I get the exception:
io.cucumber.core.exception.CucumberException: Could not convert arguments for step [I should get a SimpleListItem packet with success={booleanValue}, size={int}, data={string}, failMsg={string}] defined at 'org.flowt.shopping.services.SimpleListItem.testSimpleListItemCRUD.i_should_get_a_simple_list_item_packet(java.lang.Boolean,java.lang.Integer,java.lang.String,java.lang.String)'.
at io.cucumber.core.runner.PickleStepDefinitionMatch.couldNotConvertArguments(PickleStepDefinitionMatch.java:112)
If I REMOVE the ParameterType altogether, of course I get:
io.cucumber.cucumberexpressions.UndefinedParameterTypeException: This Cucumber Expression has a problem at column 43:
I should get a Person packet with success={booleanValue}, size={int}, data={string}, failMsg={string}
If I have it in BOTH files, I get:
io.cucumber.cucumberexpressions.DuplicateTypeNameException: There is already a parameter type with name booleanValue
So, clearly my TestRunnerGlobal.java
file is being picked up but the ParameterType
wont work if it is there and my @BeforeAll
seems to be ignored altogether.
Any suggestions about what am I missing?
Here are my dependencies:
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-redis-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets</artifactId>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkiverse.cucumber</groupId>
<artifactId>quarkus-cucumber</artifactId>
<version>0.6.0</version>
</dependency>
</dependencies>
Thanks, Murray
@abuinitski
2. I don't think Cucumber is designed to isolate stuff per-feature.
Yes, you are correct. Having said that, my tests are way faster and more efficient if the state of the database is maintained through all Scenarios in the one Feature file. e.g. CRUD testing. So I reset the db via the Background by running it just once per feature, then Create some records, Update them in different ways (including things that throw validation errors, etc), Read the new state to confirm the db state is now as expected, then finally Delete all the records.
The alternative is to repeat myself for each scenario example row eg to test the post-update Read I would need to create a new record, update it, then read it back, then delete it, and do that for each example row in that scenario. It just seems like a lot of repetitive code. Also, the feature file for that service has all the human-readable explanation of its Business Logic in the once place in a logical sequence and the Examples support the understanding of that logic. Anyway... that's just how I look at it. Maybe it isn't a good idea for some reason, but it is working.
I will have a look at the Interceptor examples to better understand what that offers.
Thanks again, Murray
Hi, Any Update on this issue?
Hi. No, I didn't solve it. I abandoned using Cucumber for Quarkus tests in the end in favour of just using the Quarkus test runner. I only need "unit tests" and I use a comment block to "replicate" the documentation aspect of using Gherkin. Not ideal, but it is good enough in my case. Sorry not to be more helpful.
Here is the similar issue with Quarkus tests, which I solved, FYI. Murray
@vikasmc Actually, I returned to this today and think I have found the solution.
Firstly, the main branch of this repo has a change made 4 months ago to implement the Cucumber @BeforeAll
and @AfterAll
annotations: commit, but that change is not in the Maven Central release for the latest version yet.
So, I cloned the current main branch and built it as-is:
git clone https://github.com/quarkiverse/quarkus-cucumber.git
cd quarkus-cucumber
mvn clean install
which installed a jar locally which I can use as:
<dependency>
<groupId>io.quarkiverse.cucumber</groupId>
<artifactId>quarkus-cucumber</artifactId>
<version>999-SNAPSHOT</version> <!-- Uses the version I built -->
<scope>test</scope>
</dependency>
I created a new Quarkus Getting Started project and added the dependency.
Following from the comment above by @christophd and the example here: https://github.com/tbounsiar/quarkus-cucumber-bdd I created a CucumberCommon.java
class like this:
package org.acme.cucumber.support;
import io.cucumber.java.AfterAll;
import io.cucumber.java.Before;
import io.cucumber.java.BeforeAll;
import io.cucumber.java.ParameterType;
import io.quarkus.logging.Log;
import jakarta.enterprise.context.ApplicationScoped;
// NOTE: This is a modified version of the file https://github.com/tbounsiar/quarkus-cucumber-bdd/blob/master/src/test/java/org/tbounsiar/steps/CommonSteps.java
// from https://github.com/tbounsiar/quarkus-cucumber-bdd
@ApplicationScoped
public class CucumberCommon {
@BeforeAll
public static void beforeAll() {
Log.info("======= CucumberCommon BEFORE ALL =======");
}
@AfterAll
public static void afterAll() {
Log.info("======= CucumberCommon AFTER ALL =======");
}
@Before
public void before() {
Log.info("======= CucumberCommon BEFORE =======");
}
// My custom boolean value parameter type
@ParameterType(value = "true|True|TRUE|false|False|FALSE")
public Boolean booleanValue(String value) {
return Boolean.valueOf(value);
}
}
I then created a test REST API endpoint:
package org.acme;
import io.quarkus.logging.Log;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import io.vertx.core.json.JsonObject;
@Path("/addition")
public class AdditionResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response addNumbers(String input) {
try {
// Get the input JSON
JsonObject jsonInput = new JsonObject(input);
int num1 = jsonInput.getInteger("num1");
int num2 = jsonInput.getInteger("num2");
Boolean addThem = jsonInput.getBoolean("addThem");
// Add the numbers if addThem is true, otherwise return 0
int sum = (addThem) ? num1 + num2 : 0;
// Make the response JSON
JsonObject jsonResponse = new JsonObject();
jsonResponse.put("sum", sum);
// Return the response
return Response.ok(jsonResponse.toString()).build();
} catch (Exception e) {
Log.error("Error processing addition", e);
return Response.status(Response.Status.BAD_REQUEST).entity("Invalid input").build();
}
}
}
Using this sample feature:
Feature: Addition Resource
Scenario Outline: Add two numbers
addThem relies on your custom boolean ParameterType(). See: CucumberCommon.java
Given I have two numbers <num1> and <num2> and addThem is <addThem>
When I call the AdditionResource API with these two numbers
Then I should get a sum of <sum>
Examples:
| num1 | num2 | addThem | sum |
| 1 | 4 | TRUE | 5 |
| 2 | 8 | true | 10 |
| 5 | 7 | FALSE | 0 |
and this step:
package org.acme.cucumber;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.cucumber.java.en.Then;
import io.restassured.http.ContentType;
import io.vertx.core.json.JsonObject;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.is;
import io.quarkiverse.cucumber.CucumberOptions;
import io.quarkiverse.cucumber.CucumberQuarkusTest;
import io.quarkus.test.junit.QuarkusTest;
@CucumberOptions(
glue = {"org.acme"},
plugin = {"json:report/index.json", "html:report/index.html"},
features = "classpath:feature"
)
@QuarkusTest
public class AdditionResourceCucumberTest extends CucumberQuarkusTest {
private String requestBody;
private String requestPath = "/addition";
@Given("I have two numbers {int} and {int} and addThem is {booleanValue}")
public void i_have_two_numbers_and(int num1, int num2, Boolean addThem) {
requestBody = new JsonObject()
.put("num1", num1)
.put("num2", num2)
.put("addThem", addThem)
.encode();
}
@When("I call the AdditionResource API with these two numbers")
public void i_call_the_addition_resource_with_these_two_numbers() {
given().contentType(ContentType.JSON).body(requestBody).when().post(requestPath);
}
@Then("I should get a sum of {int}")
public void i_should_get_a_sum_of(int sum) {
given().contentType(ContentType.JSON).body(requestBody).when().post(requestPath)
.then()
.statusCode(200)
.body("sum", is(sum));
}
}
Tests are run using the standard quarkus dev
test runner.
In the terminal I can see the Log entries for BeforeAll, Before and AfterAll as expected.
Also, my custom boolean value parameter type located in the CucumberCustom.java file works.
The only thing I couldn't work out was how to abstract the options block into a support class to avoid repeating it in every test file:
import io.quarkiverse.cucumber.CucumberOptions;
@CucumberOptions(
glue = {"org.acme"},
plugin = {"json:report/index.json", "html:report/index.html"},
features = "classpath:feature"
)
I tried various ways of creating support classes and extending etc but it just ignored the options unless it is in the actual test.
Anyway, some progress and a better understanding of how it should be set up. I will now implement this in my app and see how it all works in the "real world". Cheers, Murray
Caveat: One thing that I missed is that the @BeforeAll
and @AfterAll
must annotate static methods. That makes using @BeforeAll really tricky (impossible?) if you are testing a reactive class like io.quarkus.redis.datasource.ReactiveRedisDataSource;
. What I was testing was a reactive class wrapped around the ReactiveRedisDataSource. I have given up again and will continue to use the normal Quarkus JUnit approach. :-(
Hopefully the above is useful for someone testing a static class. Murray
Hi. I cant seem to get @BeforeAll working.
I tried like this
How should it be done? Do I need a support file somewhere? or?
Thanks, Murray