I have a step definition where I pass the name of a Scenario or Scenario outline:
#When("^I execute the steps of the following scenario or scenario outline: \"([^\"]*)\"$")
public void execute_steps_of_the_scenario_or_scenario_outline(String name){
...
}
My intent is to execute all the steps of the called Scenario/ScenarioOutline, and append those executed steps to the current scenario. This means that the steps of the called scenario become a part of the current scenarion.
However I have been unable to figure out a way to do this.
Cucumber (and BDD/TDD in general) are not meant to be used like that. Each scenario/test should be isolated from each other and is not a good practice to executes steps from one scenario on another.
You can use Background steps on a feature to execute common steps across the scenarios of that feature or you can also use tags to execute a particular set of actions before and/or after a scenario is executed using Hooks.
Having said that, you could write some logic in your glue code to maintain a list of steps (methods) which are invoked in each scenario and invoke the same list of steps on a subsequent scenario. This assumes that you could guarantee the execution order of the scenarios, which is (again) against all TDD best practices. Below is an example of the skeleton code to achieve what I have just described.
private Scenario scenario;
private Map<String, List<String>> scenarioSteps = new HashMap<>();
#Before
public void setUp(Scenario scenario) {
this.scenario = scenario;
scenarioSteps.put(scenario.getName(), new LinkedList<>());
}
#Given("^the first step is executed$")
public void the_first_step_is_executed() {
final StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[1];
scenarioSteps.get(scenario.getName()).add(stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
}
Related
Just recently I got to experience scenario parallel execution with Cucumber + JUnit 5 which works fine and I intend to use from now on. Previously since I also use cucumber-spring, I've use Spring to manage a single TestContext instance as a bean annotated with #Component which I reset every Scenario using #Before. Now that there are more scenarios running in parallel, naturally I need a threaded solution. My idea for a solution was roughly the following:
/* Store the Current Scenario in a Thread Local */
private ThreadLocal<Scenario> currentScenario = new ThreadLocal<>();
#Before
public void setup(final Scenario scenario) {
currentScenario.set(scenario);
}
/* Get Scenario from a Context Map with ScenarioId as the key */
private Map<String, TestContext> contextMap = new HashMap<>();
public TestContext getContext(final Scenario scenario) {
return contextMap.get(scenario.getId());
}
The thing is, I don't know if the Scenario starts and end in a single thread or if this proposed solution is safe at all. Is there any other way to get access to the current Scenario's Scenario instance? Any other solutions for this problem? Thank you very much.
If you're on a recent version of Cucumber, then your step definitions classes are scenario scoped by default and should not be annotated with #Component.
Each scenario gets a new instance of the step definition class.
So this is safe, even with parallel execution:
private Scenario currentScenario;
#Before
public void setup(final Scenario scenario) {
currentScenario = scenario;
}
If you have other classes without step definitions that should have a unique instance in each scenario you can combine #Component with #ScenarioScoped.
https://github.com/cucumber/cucumber-jvm/tree/main/cucumber-spring#sharing-state-between-steps
I am new to Axon framework and am trying to implement an application using CQRS with state-stored aggregates. The aggregate is created via a command handling constructor that assigns a random UUID as the aggregate identifier.
To test commands on the aggregate, I create the aggregate via a givenCommands(new CreatePlanCommand(...) call. I then want to issue the command under test, UpdatePlanNameCommand in this case, but that command needs the aggregate id that was assigned by the CreatePlanCommand executed in the test setup. Is there a way to retrieve this?
Example test code shown below (see question in comment on when() call):
#Test
public void plan_updatePlan_updatesPlanName() {
testFixture
.givenCommands(new CreatePlanCommand(123L, "My Test Plan", funds))
.when(new UpdatePlanNameCommand(/* How to get aggregate id returned by CreatePlanCommand here? */, "New Name"))
.expectSuccessfulHandlerExecution()
.expectState(state -> {
assertThat(state.getCompanyId(), equalTo(123L));
assertThat(state.getName(), equalTo("New Name"));
assertThat(state.getAvailableFunds(), equalTo(funds));
});
}
The command handler that creates the plan is as follows:
#CommandHandler
public Plan(CreatePlanCommand command, PlanFundValidator planFundValidator) {
// Use injected domain service to verify that all funds in this plan's lineup actually exist
planFundValidator.validateFundsExist(command.getAvailableFunds());
this.id = UUID.randomUUID();
this.companyId = command.getCompanyId();
this.name = command.getName();
this.availableFunds = new HashSet<>(command.getAvailableFunds());
apply(planCreatedEvent());
}
Update: I was able to get the test working by using .givenState(...) to create the aggregate as follows:
#Test
public void plan_updatePlan_updatesPlanName() {
AtomicReference<UUID> planId = new AtomicReference<>();
testFixture
.givenState(() -> {
Plan plan = new Plan(new CreatePlanCommand(123L, "My Test Plan", funds), mockPlanFundValidator);
planId.set(plan.getId());
return plan;
})
.when(new UpdatePlanNameCommand(planId.get(), "New Name"))
.expectSuccessfulHandlerExecution()
.expectState(state -> {
assertThat(state.getCompanyId(), equalTo(123L));
assertThat(state.getName(), equalTo("New Name"));
assertThat(state.getAvailableFunds(), equalTo(funds));
});
}
but this seems overly verbose and a step backwards in terms of test readability and maintenance, so I am still curious if there is a way to accomplish this with .givenCommands(...). Thank you.
All right, let me try to give you some info on that.
In my opinion, the easiest way to achieve it is to move the UUID generation to another Component. In that way, you can clearly mock it on tests.
eg:
#CommandHandler
public Plan(CreatePlanCommand command, PlanFundValidator planFundValidator, UUIDGenerator generator) {
// Use injected domain service to verify that all funds in this plan's lineup actually exist
planFundValidator.validateFundsExist(command.getAvailableFunds());
this.id = generator.generate(); // changed this line
this.companyId = command.getCompanyId();
this.name = command.getName();
this.availableFunds = new HashSet<>(command.getAvailableFunds());
apply(planCreatedEvent());
}
Doing that, same way you mocked the PlanFundValidator, you also mock the UUIDGenerator making sure you have the needed id.
For the other question:
Related question: is there a way to clear any events that were raised in the given part of a test? For example, if I have a unit test for command "C" that executes commands "A" and "B" as part of its "given" to set up the test, I would like to be able to assert that command "C" (the command under test) raised the expected events and not see any events raised by commands "A" and "B" since those are not the subject of this test and have their own tests that verify they raise the expected events.
The events are not kept from test to test but if those are raised by your own test, than you have to deal with them. By the givenState you find out earlier can also help with this. You just have to setup the state you need and fire the command/event you are testing.
In our project we are currently encountering multiple Before-/AfterScenarios, which all by definition get executed before/after every Scenario. However some of the methods are dependent on the execution of the others.
More precisely: A third party framework uses BeforeScenario in their code, that should always be executed before our BeforeScenario. JBehave has a way of prioritizing steps when it comes to chosing the correct step for execution.
#Then(value="the value returned is empty", priority=1)
public void theValueIsEmpty()
#Then("the value returned is $value")
public void theValueIs(String value)
Is there something similar for the Before-/AfterScenario annotation?
There is no way to order execution of #BeforeScenario/#AfterScenrio in JBehave. But you can try using new Lifecycle functionality:
Lifecycle:
Before:
Scope: SCENARIO
[steps to be executed before each scenario]
After:
Scope: SCENARIO
[steps to be executed after each scenario]
More details can be found in the official documentation: Lifecycle
Alternative approach: file a new JIRA ticket for Before/After prioritizing and implement it or wait for implementation from JBehave contributors.
I am writing some JUnit unit tests to test my DynamoDB data accessor object. Here is one of the test.
....
private static DynamoDBMapper ddbMapper;
#BeforeClass
public static void setup() {
ddbMapper = DynamoDBClients.getTestingDynamoDBMapper();
}
#Before
public void setupTestItem() {
Item item = new Item();
item.setItemID(TEST_COUTSE_ITEM_ID);
// Create an item with every fields populated
ddbMapper.save(item);
}
#Test
public void test_update_simple_attribute() {
Item item = ddbMapper.load(Item.class, TEST_ITEM_ID, TEST_ITEM_VARIANT);
item.setLanguageTag(TEST_LANGUAGE_TAG + "CHANGED");
ddbMapper.save(item);
Item updatedItem = ddbMapper.load(Item.class, TEST_ITEM_ID, TEST_ITEM_VARIANT);
assertEquals(updatedItem.getLanguageTag(), TEST_LANGUAGE_TAG + "CHANGED"); // This field has been changed
}
I have more tests that will update the item, and assert whether the update got pushed through to DynamoDB, and I could read it back.
However I noticed that if I run these tests more often, I sometime ran into issue that DynamoDB have not yet fully write the updated data in, and when I load it, it is still showing the old data. Rerunning the tests usually solve the issue.
I believe that DynamoDB uses eventual consistency model for write, so it makes sense that sometime update might take a bit longer than the Java execution speed. One way I could mitigate this is to have the JUnit test to suspend for 100ms or so.
But I would have to include some code to suspend execution everywhere I do a ddbMapper.save() or ddbMapper.delete(), that doesn't seem feasible for all the tests I have and the tests I will write.
It seems like I could tackle this with an annotation driven approach. Perhaps implementing a class level annotation #SuspendAtEndOfMethod and all its method could get affected, I am wondering if this is possible?
I am learning unit tests (in a TDD-like approach).
I am creating a class that encapsulates an collection, it has 3 methods:
store
retrieveAllDocuments
hasItem
I created one test: testRetrieveWhenEmpty, whici goes like this:
#Test
public void testRetrieveAllDocumentsWhenEmpty() {
List<String> storedDocs = state.retrieveAllDocs();
assertNotNull(storedDocs);
assertEquals(0, storedDocs.size());
}
And then I made it pass.
Now I would like to create a testRetrieveAllDocumentsWhenNotEmpty, which would be like this:
#Test
public void testRetrieveAllDocumentsWhenNotEmpty() {
state.store("test") //This is the only api point that I can use to insert things
List<String> storedDocs = state.retrieveAllDocs();
assertNotNull(storedDocs);
assertEquals(1, storedDocs.size());
assertEquals("test", storedDocs..get(0));
}
But now I have to implement the store method, so I created the following test method:
#Test
public void testStoreDocument() {
state.store("test")
List<String> storedDocs = state.retrieveAllDocs(); //This is the only api point I can use to see the content
assertNotNull(storedDocs);
assertEquals(1, storedDocs.size());
assertEquals("test", storedDocs.get(0));
}
I see two problems:
These methods are identical.
I test two methods in each test, if the store fails, I get a message that the retrieve has a problem.
Using reflection would bind my test to my implementation, and I am trying to avoid it.
Changing the interface for better testing is hard to argument to my team mates.
What approach do you take in these cases? (Is this case a problem at all?)
You could try shifting your view from writing "a (set of) tests per method" to "a test per useful behaviour" of the class you are testing.
If this class is both reader and writer of the data, it kinda makes sense to use its insert operations when testing the retrieval behaviour and vice versa.
I'd test the no items, one item, multiple items, has a specific item, does not have a specific item cases. These tests would implicitly test the store method as well.
Kevlin Henney's talk programming with GUTs provides a good explanation about this concept.
Actually does not need to be equals:
In testRetrieveAllDocumentsWhenNotEmpty you can add more than one document then check size, also you don't need to get all individual documents, you need to assert size is the correct.
This method asserts to get ALL documents in a non empty scenario, if you can assert you inserted 3, AND as long the testStoreDocument exists, you don't need to assert the document retrieve but yes the list size to know if ALL are returned.
#Test
public void testRetrieveAllDocumentsWhenNotEmpty() {
state.store("test1")
state.store("test2")
state.store("test3")
List<String> storedDocs = state.retrieveAllDocs();
assertNotNull(storedDocs);
assertEquals(3, storedDocs.size());
}
In testStoreDocument, you need to get the document, you don't need to assert the size of stored documents.
#Test
public void testStoreDocument() {
state.store("test")
List<String> storedDocs = state.retrieveAllDocs(); //This is the only api point I can use to see the content
assertNotNull(storedDocs);
assertEquals("test", storedDocs.get(0));
}
As mention before try to write your unit tests in a way they test scenarios. Every reader understands then better what you class is for. Since we do not just want to use a method, we want to achieve something with this method.
You can express this in the name of your tests like this: when{SomethingHappens}Then{ThisIsExpected} or given{Situation}Should{BeHandledInThisWay}
You could create tests like givenStoredDocumentsShouldBeAllReturned and whenDocumentIsStoredThenItCanBeRetrieved