What's the recommended way to run a spring boot test where only the one subject under test is configured in the context.
If I annotate the test with
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "spring.profiles.active=test")
@ContextConfiguration(classes = MyTestBean.class)
Then it seems to work - the test passes, the context starts quickly and seems to only contain the bean that I want. However, this seems like an incorrect use of the @ContextConfiguration(classes = MyTestBean.class)
annotation. If I understand correctly the class that I reference is supposed to be a Configuration
class, not a regular spring service bean or component for example.
Is that right? Or is this indeed a valid way to achieve this goal? I know there are more complex examples like org.springframework.boot.test.autoconfigure.json.JsonTest
which use @TypeExcludeFilters(JsonExcludeFilter.class)
to control the context - but this seems overkill for my use case. I just want a context with my one bean.
I know that I can just construct the one bean I am testing as a POJO without a spring context test and remove the three annotations above. But in my precise use case I am actually reliant on some of the configuration applied to the context by settings in the application-test.properties
file - which is why I've made this a Spring Boot test with a profile set. From my perspective this isn't a plain unit test of a single class in isolation of the spring context configuration - the test is reliant on certain configuration being applied (which is currently provided by the spring boot app properties). I can indeed just test the components as a POJO by creating a new instance outside of a spring context, I'm using constructor injection making the providing of necessary dependencies simple but the test does rely on things like the log level (the test actually makes assertions on certain logs being produced) which requires that the log level is set correctly (which is currently being done via logging.level.com.example=DEBUG
in a properties file which sets up the spring context).
For starters, reading the documentation first (e.g., the JavaDoc linked below in this answer) is a recommend best practice since it already answers your question.
If I understand correctly the class that I reference is supposed to be a
Configuration
class, not a regular spring service bean or component for example.Is that right?
No, that's not completely correct.
Classes provided to @ContextConfiguration
are typically @Configuration
classes, but that is not required.
Here is an excerpt from the JavaDoc for @ContextConfiguration
:
Annotated Classes
The term annotated class can refer to any of the following.
- A class annotated with
@Configuration
- A component (i.e., a class annotated with
@Component
,@Service
,@Repository
, etc.)- A JSR-330 compliant class that is annotated with
javax.inject
annotations- Any other class that contains
@Bean
-methods
Thus you can pass any "annotated class" to @ContextConfiguration
.
Or is this indeed a valid way to achieve this goal?
It is in fact a valid way to achieve that goal; however, it is also a bit unusual to load an ApplicationContext
that contains a single user bean.
Regards,
Sam (author of the Spring TestContext Framework)
It is definitely a reasonable and normal thing to only test a single class in a unit test.
There is no problem including just one single bean in your test context. Really, a @Configuration
is (typically) just a collection of beans. You could hypothetically create a @Configuration
class just with MyTestBean
, but that would really be unnecessary, as you can accomplish doing the same thing listing your contextual beans with @ContextConfiguration#classes
.
However, I do want to point out that for only testing a single bean in a true unit test, best practice ideally leans towards setting up the bean via the constructor and testing the class that way. This is a key reason why the Spring guys recommend using constructor vs. property injection. See the section entitled Constructor-based or setter-based DI of this article, Oliver Gierke's comment (i.e. head of Spring Data project), and google for more information. This is probably the reason you're getting a weird feeling about setting up the context for the one bean!
You can also use ApplicationContextRunner
to create your context using a test configuration of your choice (even with one bean if you like, but as other people have already mentioned for one bean it's more reasonable to use the constructor the classical way without using any spring magic).
What I like this way of testing is the fact that test run very fast since you don't load all the context. This method is best used when the tested bean doesn't have any Autowired dependencies otherwise it's more convenient to use @SpringBootTest
.
Below is an example that illustrates the way you can use it to achieve your goal:
class MyTest {
@Test
void test_configuration_should_contains_my_bean() {
new ApplicationContextRunner()
.withUserConfiguration(TestConfiguration.class)
.run(context -> {
assertThat(context.getBean(MyTestBean.class)).isNotNull();
});
}
@Configuraiton
public static class TestConfiguration {
@Bean
public MyTestBean myTestBean(){
new MyTestBean();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With