Adding `#SpringBootTest` annotation triggers IntelliJ false positives - java

So If you have a test class
class FooControllerIT{
#Autowired FooController controller;
}
and you add an #SpringBootTest annotation to the class, IntelliJ claims that
Could not autowire. No beans of 'FooController' type found.
Which is a lie because the tests run and pass just fine.
If I replace the #SpringBootTest annotation with, say, an #Component annotation, the "error" disappears (and re-appears when I substitute the #SpringBootTest annotation back in again).
What causes this behaviour?
(I'm on ultimate 2019.1, using Spring Boot 2.1.8-RELEASE)

The possible way that your Controller cannot be found is that it is not in the same (or higher) package as your main application class annotated #SpringBootApplication. If you don't want to move the Controller, you can create a new configuration class that will be annotated as #ComponentScan
#Configuration
#ComponentScan(basePackages = "com.your.controller.package")
public class FooConfig {
}
Even that you can add this config manually to your test spring context: #SpringBootTest(classes = FooConfig.class)

Related

Spring Boot #MockBeans - How to use the same beans in multiple test classes

I have multiple security test classes where I am testing authorization to different REST endpoints, and I would like to mock the beans of all the repositories that ApplicationContext will spin up. I have the tests running and everything works correctly, but I'd like to not duplicate my #MockBeans in every security test class.
I've tried creating a configuration class and putting the MockBeans there, but I receive a 404 error.
The current test class is setup like this and it works, but I have to duplicate the #MockBeans() in each class:
#WebMvcTest(value = [ScriptController, SecurityConfiguration, ScriptPermissionEvaluator])
#ExtendWith(SpringExtension)
#MockBeans([#MockBean(ObjectRepository), #MockBean(ChannelRepository),
#MockBean(ChannelSubscriberRepository)])
#ActiveProfiles("test")
class ScriptControllerSecurityTest extends Specification
I'd like to setup a configuration class like this and use it inside of my test class with #ContextConfiguration(classes = MockRepositoryConfiguration) in the test class.
#Configuration
#MockBeans([#MockBean(ObjectRepository), #MockBean(ChannelRepository),
#MockBean(ChannelSubscriberRepository)])
class MockRepositoryConfiguration
{}
Try creating a custom meta-annotation as follows:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.Type)
#MockBeans([#MockBean(ObjectRepository), #MockBean(ChannelRepository),
#MockBean(ChannelSubscriberRepository)])
public #interface CustomMockBeans {
}
And then annotate your test class with it:
#WebMvcTest(value = [ScriptController, SecurityConfiguration, ScriptPermissionEvaluator])
#ExtendWith(SpringExtension)
#CustomMockBeans
#ActiveProfiles("test")
class ScriptControllerSecurityTest extends Specification

SpringExtension without an explicit configuration?

I have a JUnit5 test with SpringExtension. All I need is environment variables to be injected via Spring's #Value:
#ExtendWith(SpringExtension.class)
class MyTest {
#Value("${myValue}") String myValue;
...
Doing so, I get an error saying:
Failed to load ApplicationContext Caused by:
java.lang.IllegalStateException: Neither GenericGroovyXmlContextLoader nor AnnotationConfigContextLoader was able to load an ApplicationContext
Of course, Spring needs to have a context configuration, so I put it into the test code:
#ExtendWith(SpringExtension.class)
#ContextConfiguration
class MyTest {
#Value("${myValue}") String myValue;
#Configuration
static class TestConfig { /*empty*/ }
...
While this works, it looks like a lot of unnecessary boilerplate code to me. Is there a simpler way?
UPDATE
One shorter variant would be to use #SpringJUnitConfig which brings both #ContextConfiguration and #ExtendWith(SpringExtension.class) out of the box.
But a configuration class (even an empty one) is still needed.
As has been pointed out in other answers and comments, you need to specify an empty configuration source, specifically a #Configuration class, XML config file, Groovy config file, or ApplicationContextInitializer.
The easiest way to do that is to create your own composed annotation that predefines the empty configuration.
If you introduce the following #EmptySpringJUnitConfig annotation in your project, you can use it (instead of #SpringJUnitConfig) wherever you want an empty Spring ApplicationContext.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Documented
#Inherited
#SpringJUnitConfig(EmptySpringJUnitConfig.Config.class)
public #interface EmptySpringJUnitConfig {
#Configuration
class Config {
}
}
You cannot run a Spring based test without a configuration. The Spring Test Context Framework (TCF) expects/requires an ApplicationContext. To create an ApplicationContext a form configuration (xml, Java) needs to be present.
You have 2 options to make it work
Use an empty configuration, emtpy XML file or empty #Configuration class
Write a custom ContextLoader which creates an empty application context.
Option 1 is probably the easiest to achieve. You could create a global empty configuration and refer that from the #ContextConfiguration.
In SpringBoot to run a spring application context, you need to use the #SpringBootTest annotation on the test class:
#ExtendWith(SpringExtension.class)
#SpringBootTest
class MyTest {
#Value("${myValue}") String myValue;
...
UPDATED:
Or if you use just a Spring Framework (without spring boot) then test configuration depends on a version of the spring framework which you use, and on the spring configuration of your project.
You can set configuration files by the using of #ContextConfiguration, if you use java config then it will be something like that:
#ContextConfiguration(classes = AppConfig.class)
#ExtendWith(SpringExtension.class)
class MyTest {
...
or if you use xml configuration:
#ContextConfiguration("/test-config.xml")
#ExtendWith(SpringExtension.class)
class MyTest {
...
both of these depends on your project configuration structure and list of beans which you need in tests.
More details about context configuration: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#spring-testing-annotation-contextconfiguration
If you use the Spring Framework older then 5.0 then you can find useful this library: https://github.com/sbrannen/spring-test-junit5

Spring MVC - Override a bean for one Integration test

I have a project with spring MVC v5.0.8, Java 8
I've made some integration test from controller to database, and now, I want to add one which will test what happens if the first part of a transactional behavior failed. I'll ensure that the transaction is effectively roll-backed.
So, I've to override a DAO to make it fail. After some research, came up a simple idea : override spring config for that test : Overriding an Autowired Bean in Unit Tests
My test work fine now, but the problem is that this configuration is shared with other test from other classes, even if it is in one class. How can I make it specific to that test ?
(If b creation fail, a must be roll-backed)
Failing test :
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#EnableWebMvc
#Sql({"/sqlfiles/clean-data.sql"})
public class AControllerFailDaoIt {
#Configuration
static class ConfigFailDao extends ApplicationConfiguration{
#Bean
#Primary
public BDao getBDao() {
return new BDao() {
//Overriding method to make it fail
};
}
}
#Autowired
AController aController;
#Autowired
ADao aDao;
#Test
public void should_not_create_a_if_b_failed(){
//creation of a
//assert nor a nor b are created
}
}
Nominal test case :
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#EnableWebMvc
#ContextConfiguration(classes = {ApplicationConfiguration.class, CustomWebAppConfiguration.class})
#Sql({"/sqlfiles/clean-data.sql"}) //"/sqlfiles/account-test.sql"
public class AControllerIT {
#Autowired
AController aController;
#Autowired
ADao aDao;
#Autowired
BDao bDao;
#Test
public void should_create_a_and_corresponding_b(){
//create a
//assert that both a and b are created
}
}
ApplicationConfiguration (which is test-specific)
#Configuration
#ComponentScan(basePackages = "my.base.package")
class ApplicationConfiguration implements WebMvcConfigurer {
}
Note : my Integration test classes are within the base package, does it matters ?
I found an option : put the fail test config in an external class, and call it only on my fail test, but it still doesn't work.
At the moment, I ran out of ideas, so I'll welcome your help !
I suggest you to use #Qualifier annotation.
Instead of putting #Primary above getBDao() method in your configuration put #Qualifier with some name i.e.:
#Bean
#Qualifier("BDaoTest")
public BDao getBDao() {
return new BDao() {
//Overriding method to make it fail
};
}
After that put #Primary on your default BDao implementation (in your default config).
Then when you autowire this bean in your test you need to put this qualifier:
#Autowired
#Qualifier("BDaoTest")
BDao bDao;
I got it working. In a bad way, so if you have a better option, I'm in. But I didn't found a way to make my test fail by data... Which would be the better one.
So, I've found that the #Configuration annotation make a class scanned. Spring doc.
I just made my fail configuration in an outer class, and remove the #Configuration annotation. So it won't be scanned by other test configuration. Then in my fail test class, I referenced it in the #ContextConfiguration annotation, so it is used. This way it work fine. I now have a problem with #Transactional, but that's not this thread.
Thanks to responders, you made me look in the right direction :-)

Why is WebMvcConfigurer being loaded?

I'm new to Spring and testing a Spring Data project with Postgresql/JPA and MongoDB components. My Test class has the following annotations:
#SpringBootApplication
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {SpringMongoConfig.class, PgRepository.class, MongodbRepository.class})
public class PerfTest {
#Autowired
private PgRepository pgRepo;
#Autowired
private MongodbRepository mongoRep;
For some reason the spring-context module is trying to load WebMvcConfigurer while resolving bean classes, giving me a NoClassDefFoundError.
Is the Spring Boot Autoconfigure trying to initiate a full web controller suite? If so, why? I'm really just interested in Spring Data. Should I avoid Spring Boot entirely?
You shouldn't annotate a test class as a #SpringBootApplication. This annotation is used to define a class in your main code base which contains a main() method to spin up your Spring boot container with auto-configuration.
If it's Spring data you are interested in testing then your tests will need to point to a #Configuration class which is annotated with #EnableJpaRepositories this will allow your repository interfaces to be autowired.
I don't know what is in your SpringMongoConfig class but if this contains that annotation then all you need to do is:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoConfig.class})
public class PerfTest {
If not then you need to set up this configuration and include it in the #ContextConfiguration of the test.
Another option which is a bit more heavyweight but will guarantee everything is available for your test (provided your Spring boot application is set up correctly) is to mark your test with #SpringBootTest this will automatically load the whole Spring boot context for the application. If it has trouble finding your main class you can point it in the right direction by providing the class in the annotation:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyMainClass.class)
public class PerfTest {
You need to add #Component to your repository interface. Spring loads the component class first and then #AutoWiring & Initialization of variables will happen.

How can I use ActiveProfiles with Spring Boot tests with WebMvcTest?

I have a unit test that is testing a RestController using #WebMvcTest. The the controller class autowires a service class that I would like to mock. I found that I can use #Profile and #Configuration to create a config class for specifying primary beans to use when a profile is active. I tried adding an active profile to my unit test class, but it says it failed to load the ApplicationContext. I'm not sure how I can do that while using #WebMvcTest.
#ActiveProfiles("mockServices")
#RunWith(SpringRunner.class)
#WebMvcTest(VoteController.class)
public class VoteControllerTest {
...
It seems I may be approaching this wrong. Any help is appreciated.
Edit:
Here is my configuration class:
#Profile("mockService")
#Configuration
public class NotificationServiceTestConfiguration {
#Bean
#Primary
public VotingService getVotingService() {
return Mockito.mock(VotingService.class);
}
}
The error I'm actually getting is:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'VotingService'
I was able to solve it by using #MockBean for VotingService in my Unit Test class. However, I want to use the Profile configuration I have in NotificationServiceTestConfiguration without having to call out mock beans.
Any thoughts?

Categories

Resources