spring application context in a set of unittest classes - spring

I have created a couple unittest classes in the same package. All these classes have exactly one testcase and have the same annotation as shown below:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes= {TestConfig.class} )
When I run these testcases, I want each testcase be run in its own application context. But it seems all the testcases in the same package share one single application context, be it run from maven command line, or in Eclipse select the package to run as junit.
If I duplicate the TestConfig wiht names like TestConfig1, TestConfig2, etc. and annotate different test class with different TestConfig class, then each test will run in its own context instance.
Is there other elegant method to achieve this?
Thanks a lot.

This is the default behavior of Spring test. Spring test default caches the application context across the tests. This decreases the test execution time.
I dont know about your use case. If u have any where u are dirtying the application context(changing the state of the beans that are managed by springs that is affecting the subsequent tests ) then u can go for annotating the test method with #DirtiesContext. Spring test reloads the context for these methods. See below link how to use DirtiesContext.
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#testcontext-ctx-management-caching
Use this feature carefully whenever needed only as this may shoot up your test execution time exponentialy.

Related

DataSource per test with Cucumber and SpringBoot

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
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 BackendSupplierimplementation 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!

Kotlintest with Spring Test, #Transactional not working/applied

I have a problem getting Spring Boot 2.0.5 to work nicely with Kotlintest 3.1.10.
I made a test project illustrating the problem I have.
The project is a Spring Boot 2 application
with two entities, ShoppingOrder and OrderLine (to be totally unimaginative).
There is also a test case ShoppingOrderSpec which just tests the mapping by storing and retrieving the Order.
The testcase is configured like this:
#ExtendWith(SpringExtension::class)
#Transactional
#SpringBootTest
class ShoppingOrderSpec : WordSpec() {
override fun listeners() = listOf(SpringListener)
The test case is using the SpringExtension
by Spring to hook into the JUnit 5 engine. It also uses the SpringListener and Wordspec from Kotlintest to structure the tests
and do the assertions.
The SpringListener correctly autowires the dependencies, but somehow the transaction is not being created.
Running the testcase gives the following stack-trace:
2018-10-12 10:54:14.329 INFO 59374 --- [intest-engine-0] com.example.demo.ShoppingOrderSpec : Started ShoppingOrderSpec in 4.478 seconds (JVM running for 7.421)
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.demo.ShoppingOrder.lines, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:582)
...
at com.example.demo.ShoppingOrderSpec$1$1.invoke(ShoppingOrderSpec.kt:35)
at com.example.demo.ShoppingOrderSpec$1$1.invoke(ShoppingOrderSpec.kt:19)
So, somehow the org.springframework.transaction.annotation.Transactional annotation does not seem to work,
as removing the annotation, just gives the same response.
Anyone any ideas how to get the #Transactional being applied and respected?
You can't use JUnit Jupiter extensions with KotlinTest as they are different engines. Junit Jupiter is an implementation on top of Junit Platform, like KotlinTest is, but anything written specifically for Jupiter won't work with KotlinTest. Anything written for Junit Platform should work however.
Unfortunately, the naming choices by the JUnit team are poor imo, and so people think JUnit Jupiter is the same thing as JUnit Platform.
Anyway, those
#ExtendWith(SpringExtension::class)
#Transactional
#SpringBootTest
extensions are not going to mean anything to KotlinTest, anymore than they would for Spek or whatever. ExtendWith is a Jupiter specific annotation that tells it to use the SpringExtension class. The KotlinTest equivilent is SpringListener which you've already wired in.
I'm not sure if #SpringBootTest will be picked up or not by Spring. Support may need to be added for that depending on what it does.
Finally #Transactional works by creating proxies on the methods, but since in more advanced testing frameworks like KotlinTest, the test containers are not methods, but just arbitrary functions, it won't be able to intercept.
I think in this case, you might need to create a proper method and annotate that, or try using the AnnotationSpec rather than StringSpec or whatever other spec base class you are using, which uses actual methods that you could annotate.

How to define the spring.config.location on Spring Boot and JUnit tests?

How we can programmatically configure Spring Boot to define new values to the spring.config.name and spring.config.location properties when running JUnit tests?
For example, if we would like to define these properties when running the application itself we could use something like (in Kotlin):
fun main(args: Array<String>) {
// SpringApplication.run(Application::class.java, *args)
val applicationContext = SpringApplicationBuilder(Application::class.java)
.properties(
"""spring.config.name:
${getSpringConfigNames()}
""",
"""spring.config.location:
${getSpringConfigLocationPaths()}
"""
)
.build()
.run(*args)
// val environment = applicationContext.getEnvironment()
}
But I wasn't able to find a way to configure this to use in the JUnit tests.
Edit
There is a complication here because of an spring boot limitation.
I would like to use an entire folder and its subfolders as valid locations to search for configuration files (so, for example, we could have folders for specific environments, databases, third-parties, and so on).
When running the application this was possible creating a method, in this case getSpringConfigLocationPaths(). And this method create a comma separated list with all folder inside the "main" folder.
For example, for the main folder src/main/resources/configuration it will output:
src/main/resources/configuration,
src/main/resources/configuration/environments,
src/main/resources/configuration/environments/development,
src/main/resources/configuration/environments/staging,
src/main/resources/configuration/environments/testing,
src/main/resources/configuration/environments/production,
src/main/resources/configuration/environments/common
How could we solve this situation when using JUnit tests and Spring Boot?
Unfortunately Spring Boot doesn't allow something like src/main/resources/configuration/**/*.
Because we have organized the system with several properties files on different subfolders we need to find a way to dinamically consider them.
I am using latest Spring Boot 2.2.0 and from my experience both #TestPropertySource and #SpringBootTest annotations can do the job because they have properties attribute.
So, you can do something like this:
#TestPropertySource(properties = ["spring.config.location=classpath:dev/", "spring.config.name=custom-app-name"]
#TestConfiguration
class DevTestCfg {} // this will make tests to look for configs in resources/dev/custom-app-name.properties
Also notice that there is a spring.config.additional-location property if you want your properties to be loaded from multiple locations.
The only problem here is that values in properties attribute must be constant.
But you can create multiple configurations for each environment and put corresponding #Profile("envName") on each configuration class. Then run your tests with different -Dspring.profiles.active and corresponding test configuration should be automatically picked up.
The tests that run spring boot should be carefully designed,
There is a whole testing framework for spring boot tests, so obviously consider using this framework.
When it comes to configuration management, I suggest considering the following:
There are two types of tests basically:
Tests that load a concrete specific configuration (set of beans), for example if you want to test only a DAO, you load a configuration for this dao.
In this case, the configuration is something that should be "tailored" to the needs of a specific test, and no "full" configuration is required.
For example, if the microservice contains a configuration for a database (user, password, schema, etc) and for, say, messaging management, there is no need to specify a configuration of a messaging system when testing a DAO, messaging beans won't be loaded anyway.
Usually, the test of this "type" will look like this:
#SpringBootTest(classes = {RelationalDbDaoConfiguration.class})
public class MyDaoTest {
}
If you don't have a configuration for your needs you can use #MockBean to mock unnecessary beans or even create a custom configuration in src/test/java so that it will be only in test classpath. It makes sense to use #TestConfiguration but it's beyond the scope of the question.
Now in order to load the configuration for db only, the are many options, to name a few:
#ActiveProfiles("dao") on a test class + putting "application-dao.properties/yaml" into the src/test/resources or src/test/resources/config
Use #TestPropertySource(locations = "classpath:whatever.properties") on test
Create a special "DbProperties" bean and initialize it programmatically in spring, it can make sense when you know some details about the context in which the test runs only during the actual test execution (for example, if you start a database before the test and the port is created dynamically, but its really a fairly advanced setup and is beyond the scope of this question) + the data source bean can read these properties
Use #SpringBootTest's properties attribute to provide 'fine-grained' properties definitions
Kind of obvious, but I'll mention it anyway: put application.properties in src/test/resources it will override regular configurations
The second type of tests is when you load the "entire" microservice, usually, these are tests that do not have "classes" parameter in #SpringBootTest annotation
#SpringBootTest // note, no actual configurations specified
public class MyMicroserviceTest {
...
}
Now, this definitely requires to specify a whole set of configurations, although the techniques for actually specifying these configurations are still applicable (just the content of configuration files will be different).
I do not suggest the usage of spring.config.location during the test, because this means that the test depends on some external resource, which makes the whole setup even more complicated.
If it's XML driven configuration,
#ContextConfiguration(locations = "/app-context.xml")
If it's annotation driven by configuration classes,
#ContextConfiguration(classes = {AppCOnfig::class, AnotherCOnfig::class}
These would be defined on the class level on the unit test class you run.
Further, if you have profiles for Junit to consider,
#ActiveProfiles("myProfile") would be added to the test class.

Spring Cloud Task Integration Testing

I’m looking for some guidance on the best way of writing integration (ie tests for entire Spring Boot Application) for Spring Cloud Task.
Based on existing documentation and samples I see two approaches for this:
1) Use the standard #SpringBootTest with #TestPropertySource(properties = {"spring.cloud.task.closecontext_enable=false"}
as described here
http://docs.spring.io/spring-cloud-task/docs/1.2.0.M2/reference/htmlsingle/#_writing_your_test
This seems to allow effectively only one test per test class as the task is run when the spring context is initialized ie once per test class. However
#Autowiring of beans in the context into the test class should work to eg to check the results of the task, or examine the state of the task repository.
2) Use SpringApplication.run(MyTaskApplication.class, myArguments); in each test method as in the example here
https://github.com/spring-cloud/spring-cloud-task/blob/master/spring-cloud-task-samples/batch-job/src/test/java/io/spring/BatchJobApplicationTests.java
This allows me to write multiple tests in the test class each with potentially different spring properties or batch job parameters.
The main problem I have with either approach that I can’t see how to get access eg #Autowire to beans in the context such as JdbcTemplate (eg to insert test input data for a job into an embedded db) or RestTemplate (to set up expectations using MockRestServiceServer)
after these beans are created but BEFORE the task is run - is this possible? If not it’s hard to see how to write meaningful integration tests for tasks.
What Ive done for now is a variation on approach (2) above (ie I can run the task more than once / have multiple tests in the same test class)
I'm using
SpringApplication application = new SpringApplication(new Object[] {MyTaskApplication.class, TestConfig.class});
TestConfig is defined with #TestConfiguration in the test class and contains mock beans etc overriding the actual beans
I then use
application.addListeners()
to add a ContextRefreshedEventListener which allows me to set expectations on mocks (or execute jdbc calls) after the beans are created but before the task is run.
(I have a generic Listener class that allows me to pass in the behaviour as a lambda or method reference per bean)
Then run the task with
application.run(args);
(can use different args in different tests)
I can also pass "--spring.cloud.task.closecontext_enable=false" as an argument to keep the application open if i want to verify mocks / db state after the test runs. In this case I close it manually at the end of the test.
If this seems a sensible approach it might be useful if Spring Cloud Task itself provided some sort of generic listener or hook to allow the setting of test state between bean creation and task execution.

Running testng with Spring context in Intellij Idea

When I run tests in my maven module from cmd I see that Spring context is available for all my tests even when they don't extend AbstractTestNGSpringContextTests and are not annotated with #ContextConfiguration.
However, when I run all tests in the test dir from Idea, some of the tests fail with NPE because #Autowired fields are not initialized. Most confusing is that, as I said, some tests pass and others don't even though they all don't extend AbstractTestNGSpringContextTests and are not annotated with #ContextConfiguration but all require Spring-injected fields in some of the classes. When I run tests separately in Idea, they always fail with NPE because there's no Spring injection. I'm new to testng and can't understand how the Suites are created and run with Spring context.
By the way, we tried it on Ubuntu machine, and the behaviour is not the same. Separate tests failed, but running the package succeeded without injection-related NPE issues.
Anyone encountered anything similar?
The problem seems to revolve around IntelliJ/testNG not processing 'groups' correctly. If a class "A" defines an autowired object, the #beforeGroups method in that class cannot use that object. But a different class "B" that extends class "A", and "B" has a #Test that uses the autowired object, then "B" can use the object (which is defined in "A", where it doesn't work!!!). Problem does not occur in Eclipse or with a Maven goal where surefire controls testNG.
The workaround for IntelliJ is to run an entire suite, instead of just a method or class. If we create a new suite file and strip out all the other classes, then we can run just one class.
See these issues in YouTrack
https://youtrack.jetbrains.com/issue/IDEA-135384
https://youtrack.jetbrains.com/issue/IDEA-125775
https://youtrack.jetbrains.com/issue/IDEA-110703
https://youtrack.jetbrains.com/issue/IDEA-111084

Resources