Reputation: 101
I have a cucumber test setup with spring boot. There are a large number of integration tests which take a while to run. Because they share a database, the new threaded mode in cucumber 4+ does not work (as expected).
Ideally this would work in Junit also.
For each test, I would like to create a dynamic datasource with a new database/datasource instance that the test can use independently of others, allowing it to run multithreaded (and use the 12 cores I have available).
I have tried @Scope("cucumber-glue")
and @Scope("prototype")
on the DataSource bean, but this results in org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'dataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?
.
If I use the prototype scope, the bean creation method gets called each time, but gives this error, as does the glue scope.
I have also added @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
to classes and methods, without the scope, but this seems to do nothing.
Is there a way I can either: 1. create a new datasource for each test and have hibernate create the tables? 2. pool a set of datasources that could be used by tests? 3. Populate/reinitialize the context accordingly?
A side consequence of this is that I don't believe my context is getting recreated properly between tests for other instances of the scoping.
@Configuration
public class H2DynamicDataSource {
@Autowired
private Environment env;
@Bean
@Scope("prototype")
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
}
Cheers R
Upvotes: 0
Views: 1254
Reputation: 81
Hopefully you've solved this. I just went through something similar and felt like cruising around SlackOverflow to see if anyone was doing something similar (or perhaps asking about how to do something like this) and saw this post. Figured this would be a good place to drop this information in case anyone else tries to go down this road.
I think your asking two questions:
How to get multithreading working with Cucumber and Junit Cucumber?
multithreading works great but you are constrained to a single lane
of test execution by junit if your using either the Cucumber
or
SpringJUnit4ClassRunner
runner junit runner classes. (I suspect this is why @CucumberOptions doesnt contain a thread arg. It's useless with the current Cucumber runner implementation.)
In order to get this working I had to write my own test runner that
would defer a the entire test execution to Cucumbers Runtime
object. I used a custom cucumber plugin to cache the results and
then relay those results back to junit when asked to 'run' the
individual tests. With Cucumber behind the wheel I was able to allow
any arbitrary number of threads for my tests.
How to ensure a new spring datasource for each test?
If you're unfamiliar with how Spring cache's contexts for tests you should have a look through the documentation here https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-ctx-management-caching but the tl;dr: Spring's TestContextManager
(this is whats under the hood of Springs junit runner) will create and store a spring context in a map keyed by your test configuration. DirtiesContext will force the context to reload.. but otherwise two tests with the same parent class are effectively guaranteed to execute in the same test context. If your datasource is a spring datasource that is initialized during boot.. you definitely need to refresh this context between tests.
Putting those two concepts together.. Cucumber-Spring provides a way to create a new context (consistent with cucumbers new 'world' per test design) for every test but it does so by disregarding any existing spring contexts or data contained therein. This may actually be helpful to you if you can trust Cucumber-Spring to correctly stand up your datasource for you.. but in my situation we had a bunch of issues using this fake context and really needed to pull objects from the default spring context. In my case I had to incorporate Springs TestContextManager
into my custom plugin AND write my own Cucumber BackendSupplier
implementation to hijack Cucumbers dependency injection mechanism (effectively replacing Cucumber-Spring)
All in all, what your trying to do is a major PITA and I hope the Cucumber folks make this easier at some point.. but what your trying to do is definitely possible. Hope you got it working!
Upvotes: 1