I want my #Configuration to not be active during tests with #SpringBootTest.
Is there any configuration property or bean active automatically in every test to detect if the ApplicationContext is in test?
For example:
#Configuration
#ConditionalOnMissingBean(TestEntityManager.class)
But I can't use TestEntityManager because that is only in a #DataJpaTest context.
I want to avoid going into every #SpringBootTest and doing configurations for each.
After some debugging, I think I found a common bean that only exists in the test context:
#ConditionalOnMissingBean(type = "org.springframework.boot.test.mock.mockito.MockitoPostProcessor")
Related
In Spring Boot, is there a way to mock a single bean for all existing JUnit tests, without changing the existing test classes (e.g., by adding an annotation or adding inheritance)? Like injecting a bean globally via configuration.
Assuming you are using #SpringBootApplication in your main sources to define the Spring Boot application, you'll already have component scanning enabled for everything in that package (including nested packages).
When running tests, the classes (typically) in src/test/java are also added to the classpath, and are therefore available to be scanned as well.
For example, if you defined your #SpringBootApplication at com.example.boot.MySpringBootApplication, then com.example.boot.MyTestConfiguration would be eligible for component scanning, even though the former is in src/main and the latter in src/test. Putting it in the src/test/java directory would ensure that it only has an effect while running tests.
You can then define any "global" beans you would like in that configuration.
Using the package/class names I provided:
// File: src/test/java/com/example/boot/MyTestConfiguration.java
#Configuration // this will get component-scanned
public class MyTestConfiguration {
#MockBean
MyBean myGlobalMockBean;
}
Then, so long as you don't omit that Configuration from the Context Configuration, the MockBean should always be present under test.
I am new to Spring and spring boot.
For my spring boot application which is a rest controller, I have some beans along with my data source.
I use my data source to create jdbc template. Now when I am in my rest controller code, I have all these beans #Autowired and they work perfectly fine.
My query is regarding the junit testing part.
When I write my test code inside src/test/java and when I execute my test class within IDE, are the beans defined in my src/main/javacode, instantiated before test case execution?
You might use the same container, or instantiate another container particularly for testing purposes, for which you'll provide a configuration of that other Spring Container separately:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:test-context.xml")
public class SomeClassTest{...}
However, you can also enable support for loading your Application Context and then use the #Autowired fields in your JUnit fixtures, which also works fine too:
#RunWith(SpringRunner.class)
public class SomeTestClass {
....
#Autowired
ApplicationContext context;
....
}
From here, you can get any bean you wish.
I have a bunch of tests in my project that are all annotated with #SpringBootTest and therefore load up a SpringBoot context.
Now recently I refactored a Test in which I wanted a smaller scope (it´s about process coverage with camunda) to #RunWith(SpringJUnit4ClassRunner.class).
Since this means that no context is loaded automatically I create some beans "manually" with a static inner class configuration. The entire test looks something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {
ExternalConfiguration.class, MyTest.InternalConfiguration.class
})
public class MyTest{
#Autowired
private SomeBean someInternalBean;
#Configuration
public static class InternalConfiguration{
#Bean
SomeBean someInternalBean() {
return mock(SomeBean .class);
}
}
//Tests
Now, this test runs fine when I run it. BUT when I run any other test ( those still annotated with #SpringBootTest), I get issues with when the ApplicationContext is loaded:
The bean 'someInternalBean', defined in class path resource [.../MyTest$InternalConfiguration.class], could not be registered. A bean with that name has already been defined in file [.../SomeBean.class] and overriding is disabled.
Apparently a bean is created when loading the ApplicationContext because the class is annotated with #Component AND the context loader tries to create another bean from my internal configuration.
I cant allow bean-overriding because my mock beans might overwrite the automatically created beans (which they do, I tried).
How do I circumvent this? I want my SpringJUnit4ClassRunner-tests with their internal configurations to not affect my other #SpringBootTest-tests. I already tried making the configuration beans conditional with #ConditionalOnMissingBean but that did not work.
Turns out those inner configuration classes should not be annotated with #Configuration. Removing the annotation makes it so that the manual bean generation still works and the configuration is no longer picked up by the componentScan.
So I have a test class annotated with #SpringBootTest
#SpringBootTest
#Slf4j
public class IntegrationTests {}
This code automatically starts two of classes in my application annotated with #service.
In my integration test I’m testing the spring scheduler to works as expected and the tests passes.
Now I want to write another test that only test one of the two services annotated with #service. The second service I want to enable and disable myself. This is because one of two services uses the springboot #scheduled annotation, which results in unpredictable logic.
So is there a way I can start a #SpringBootTest with the possibility to choose the services I need?
Thanks in advance!
Have you tried adding the test classes in #SpringBootTest? like so:
#SpringBootTest(classes = YourTestClass.class)
What does this annotation do?
When would I want to use it?
When would I not want to use it?
#RunWith(SpringJUnit4ClassRunner.class)
I can find more usages of this when I Google and do not find a 101 explanation as to what this annotation is supposed to communicate to me or when/why I would use it?
The annotation is used to configure a unit test that required Spring's dependency injection.
From Spring Reference - 10. Unit Testing:
10.1 Creating a Unit Test Class
In order for the unit test to run a batch job, the framework must load the job's ApplicationContext. Two annotations are used to trigger this:
#RunWith(SpringJUnit4ClassRunner.class): Indicates that the class should use Spring's JUnit facilities.
#ContextConfiguration(locations = {...}): Indicates which XML files contain the ApplicationContext.
If you are using annotations rather than XML files, then any class that you are unit testing that requires Spring dependency injection needs to be put into the #ContextConfiguration annotation. For example:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = FooManager.class)
class FooManagerTest {
#Autowired
FooManager fooManager;
Now when you use fooManager in a unit test it will have have a Spring context setup for it.
If fooManager autowires in any beans then those bean's classes also need to be in the #ContextConfiguration annotation. So if fooManager autowires in a FooReporter bean:
#ContextConfiguration(classes = {FooManager.class, FooReporter.class})
If the beans that fooManager autowires in contain state, then you will likely want to reset the state of those beans for each test. In that case you can add the #DirtiesContext annotation to your test class:
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
If fooManager or any of its autowired beans reads Spring config then you need to add an initializers list to the #ContextConfiguration annotation, that contains the ConfigFileApplicationContextInitializer class:
#ContextConfiguration(classes = {FooManager.class, FooReporter.class}, initializers = ConfigFileApplicationContextInitializer.class)
To answer the when you would and wouldn't want to use it part of the question.
When to use SpringJUnit4ClassRunner
IMO SpringJUnit4ClassRunner should be used very sparingly. There is a significant overhead involved with starting up a Spring container to run a unit test.
I typically use SpringJUnit4ClassRunner to test:
that components are injected (auto-wired) as expected
that configuration data is injected as expected
When you are injecting components issues can arise if the #Qualifier annotation is not used or used incorrectly, for example.
When loading configuration from multiple yaml files you may want to test that maps are being merged as expected, with the appropriate overrides occurring.
At the very least I always have a simple SpringJUnit4ClassRunner test as a sanity check that the Spring container starts up OK.
When not to use SpringJUnit4ClassRunner
I would not use SpringJUnit4ClassRunner to test the non-Spring related functionality in my code under test. Which in my experience means most of the functionality.
So this means that any autowired components and injected config data needs to be mocked. This can mean quite a bit of setup code for your unit tests. However this setup code only needs to be written once for all the tests in your class under test. It is also much quicker to run unit tests with mocked components.
I keep the mocking simple and use Spock to mock the components. Example groovy code:
import spock.lang.Specification
class FooManagerTest extends Specification {
FooManager cut
void createMockFooReporter() {
FooReporter mockFooReporter = Mock(FooReporter)
mockFooReporter.fooFormatter = Mock(FooFormatter)
}
void setup() {
cut = new FooManager()
cut.fooReporter = createMockFooReporter()
}
void "Basic test"() {
// Do a basic test using 'cut'
}
}
In this example the class under test FooManager has an autowired FooReporter which itself contains an autowired FooFormatter.
I think #RunWith annotation is in order to initialize the context of spring. Because the junit5 is released, you just can replace it with #SpringJUnitConfig.By the way, #RunWith annotation is already replaced by #ExtendWith, but you still can use it.