Why is WebMvcConfigurer being loaded? - java

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.

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

Adding `#SpringBootTest` annotation triggers IntelliJ false positives

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)

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 Boot Test - I do not want to load all #Configuration classes for a particular test

I have a half-dozen classes in an application annotated with #Configuration and am writing unit tests.
I have one #Configuration class that sets up Quartz, and one that deals with setting up Camel.
In my Camel unit test, I only want to load the #Configuration class that sets up Camel, because it does not care about Quartz (and vice-versa).
How do I tell my Spring Boot test to only bootstrap certain configuration-annotated classes? #TestConfiguration does not do that...
#Configuration
public class QuartzConfig {
...
}
#Configuration
public class CamelConfig {
...
}
#RunWith(SpringRunner.class)
#SpringBootTest
#SOME ANNOTATION THAT SAYS ONLY LOAD CamelConfig.class???????
public class CamelOnlyTest {
....
}
By using the classes parameter of #SpringBootTest, you can specify which classes to use for configuration.
From the docs:
public abstract Class[] classes
The annotated classes to use for loading an ApplicationContext.

Spring Injecting Beans from src folder in test folders

I have utility classes exposed as beans in my source folders. I want to use some of those utilities in my test classes written in junit 4. For example , I have a utility class that has methods which marshal an object into JSON string. I want to inject this utility bean in my test class. I am unable to inject these beans using Autowired annotation. Should I copy all these classes over to test folder?
Edit:
I am trying to inject jsonUtil. Below is how my code looks like.
import static org.junit.Assert.*;
import java.math.BigDecimal;
#RunWith(MockitoJUnitRunner.class)
#SpringApplicationConfiguration(classes = ProxyApplicationMock.class)
public class ProxyApplicationMock {
#Mock
public SoapClient soapClientMock;
private JsonUtil jsonUtil;
Main Class
public class ProxyApplication {
public static void main(String[] args) {
SpringApplication.run(ProxyApplication.class, args);
}
}
Your main classes can be seen by your test classes, but not the other way around. So no, you don't need to copy them.
If your utility class is declared as a Spring managed bean in your test Spring context configuration (the class -or XML file- declared in the #ContextConfiguration) which may and probably should be different from your main configuration.
Then you can inject it in any Spring managed class, which includes your test classes if it's using the SpringJUnit4ClassRunner.
EDIT:
To sum up what we discussed in the comments, the main problem is that your test runner is not a SpringRunner (alias for SpringJUnit4ClassRunner), and thus JUnit is not running your test in a Spring context. Have a look at a test example here.
The simplest test case will look like this.
#RunWith(SpringRunner.class)
#SpringBootTest
public class CityRepositoryIntegrationTests {
#Autowired
private MySpringBean springBean;
//...
}
But as often with Spring Boot, there's some magic happening behind. #SpringBootTest is a convenient annotation that will detect automatically a class annotated with #SpringBootConfiguration, meaning if you don't have a specific Spring configuration for your test, it will use your main Spring configuration, and thus include and instanciate all the beans for your main app, and that's not usually what we want in a unit test cause we want to test a class independently by mocking its dependencies.
What you can do, is provide the Spring compenent classes you want to include in your tests, as such:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MySpringBean.class)
public class CityRepositoryIntegrationTests {
#Autowired
private MySpringBean springBean;
#Mock
private MyMockedSpringBeanDependency mocked;
//...
}
This question is to Matt, since adding comment is throwing error saying only one additional use can be notified.
NOTE: Not an Answer
I have an Application class & many config classes which are imported in Application class. Earlier it was #Configuration, which I converted to #SpringBootConfiguration in Application class and the actual Config class, whos bean I am trying to mock. Ended up in
java.lang.NoSuchMethodError: org.springframework.boot.SpringApplication.<init>([Ljava/lang/Object;)V
at org.springframework.boot.test.context.SpringBootContextLoader.getSpringApplication(SpringBootContextLoader.java:121)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:84)
Please suggest How do I mock, I have the same setup as specified in the ticket.
#Bean
public CacheManager cacheManager()
{
EhCacheManagerFactoryBean factoryBean = new EhCacheManagerFactoryBean();
factoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
factoryBean.setShared(true);
return new EhCacheCacheManager(factoryBean.getObject());
}
}

Categories

Resources