I am writing an integration test for my application, and want to use a custom webmvc configuration for my tests
I have three classes in my base package com.marco.nutri:
Application(which is annotated with #SpringBootApplication)
MvcConfig(#Configuration and #EnableWebMVC)
SecurityConfig(#Configuration and #EnableWebSecurity)
My test is in the package br.com.marco.nutri.integration.auth:
#RunWith(SpringRunner.class)
#SpringBootTest(classes={Application.class, WebMvcTestConfiguration.class, SecurityConfig.class})
public class ITSignup {
//Test code
}
I have a test config class in the package com.marco.nutri.integration:
#TestConfiguration
#EnableWebMvc
public class WebMvcTestConfiguration extends WebMvcConfigurerAdapter {
//Some configuration
}
But when I run my test, the MvcConfig.class is picked instead of WebMvcTestConfiguration.class
What am I doing wrong?
you can annotate your test configuration with #Profile("test") and your real one with #Profile("production")
and in your properties file put the property spring.profiles.active=production and in your test class put #Profile("test"). So when your application starts it will use "production" class and when test stars it will use "test" class.
from documentation
Unlike regular #Configuration classes the use of #TestConfiguration
does not prevent auto-detection of #SpringBootConfiguration.
Unlike a nested #Configuration class which would be used instead of a
your application’s primary configuration, a nested #TestConfiguration
class will be used in addition to your application’s primary
configuration.
Related
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
In my Spring boot 2.1 project I have different #Configurations for different test (ConfigurationA and ConfigurationB), that reside in different packages. Both configurations define the same set of beans but in a different manner (mocked vs. the real thing)
As I am aware of the Bean overriding mechanism introduced in Spring Boot 2.1, I have set the property: spring.main.allow-bean-definition-overriding=true.
However I do have a test with the following the setup of the following configuration and test class. First there is a #Configuration in the productive part (I'm using Maven):
package com.stackoverflow;
#Configuration
public class ProdConfiguration{
...
}
Then in the test branch there is a general Test #Configuration on the same package level:
package com.stackoverflow
#Configuration
public class TestConfiguration {
#Bean
public GameMap gameMap() {
return Mockito.mock(GameMap.class);
}
}
And in a subpackage I have another #Configuration:
package com.stackoverflow.impl;
#Configuration
public class RealMapTestConfiguration {
#Bean
public GameMap gameMap() {
return new GameMap("testMap.json");
}
}
And then of course there is the test that is troubling me:
package com.stackoverflow.impl;
#ExtendWith(SpringExtension.class)
#SpringBootTest
#ContextConfiguration(classes={RealMapTestConfiguration.class, ProdConfiguration.class})
#ActiveProfiles("bug") // spring.main.allow-bean-definition-overriding=true
public class MapImageServiceIT {
#Autowired
private GameMap map;
}
It turns out that the injected GameMap into my test is a mock instance from TestConfiguration instead of the real thing from RealMapTestConfiguration. Aparrently in my test I have the configuration from ProdConfiguration and TestConfiguration, when I wanted ProdConfiguration and RealMapTestConfiguration. As the beans defined in the ProdConfiguration and *TestConfiguration are different the combination works, but TestConfiguration and RealMapTestConfiguration define the same been. It seems like the TestConfiguration is picked up by component scanning as it is in the same package as ProdConfiguration.
I was under the impression that when overriding beans the bean definition that is closer to the test class would be preferred. However this seems not to be the case.
So here are my questions:
When overriding beans, what is the order? Which bean overrides which one?
How to go about to get the correct instance in my test (using a different bean name is not an option, as in reality the injected bean is not directly used in the test but in a service the test uses and there is no qualifier on it.)
I've not used the spring.main.allow-bean-definition-overriding=true property, but specifying specific config in a test class has worked fine for me as a way of switching between objects in different tests.
You say...
It turns out that the injected GameMap into my test is a mock instance from TestConfiguration instead of the real thing from RealMapTestConfiguration.
But RealMapTestConfiguration does return a mock
package com.stackoverflow.impl;
#Configuration
public class RealMapTestConfiguration {
#Bean
public GameMap gameMap() {
return Mockito.mock(GameMap.class);
}
}
I think the problem here is that including ContextConfiguration nullifies (part of) the effect of #SpringBootTest. #SpringBootTest has the effect of looking for #SpringBootConfiguration in your application (starting from the same package, I believe). However, if ContextConfiguration is applied, then configurations are loaded from there.
Another way of saying that: because you have ContextConfiguration in your test, scanning for #Configuration classes is disabled, and TestConfiguration is not loaded.
I don't think I have a full picture of your configuration setup so can't really recommend a best practice here, but a quick way to fix this is to add TestConfiguration to your ContextConfiguration in your test. Make sure you add it last, so that it overrides the bean definitions in the other two configurations.
The other thing that might work is removing #ContextConfiguration entirely and letting the SpringBootApplication scanning do its thing - that's where what you said about the bean definition that is closest may apply.
In that case just don't use #Configuration on configuration class and import it to the test manually using #Import, example:
#SpringBootTest
#Import(MyTest.MyTestConfig.class)
public class MyTest {
#Autowired
private String string;
#Test
public void myTest() {
System.out.println(string);
}
static class MyTestConfig {
#Bean
public String string() {
return "String";
}
}
}
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.
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.
In my unit test class, I have the following configuration:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = WebAppConfigTest.class)
public class ExampleTest {
But after loading the WebAppConfigTest class, it's loading my WebAppConfig class that has #Configuration and is out of the test package (src/test/java).
Note: the class WebAppConfig is not configured to be loaded into the unit test, but still is being charged.
WebAppConfig Class
#EnableWebMvc
#ComponentScan(basePackages = {"br.com.example"})
#PropertySource(value="classpath:application.properties")
#Configuration
public class WebAppConfig extends WebMvcConfigurerAdapter {
WebAppConfigTest Class
#ComponentScan(basePackages = {"br.com.example"})
#Configuration
public class WebAppConfigTest {
How to prevent this class out of the test package is loaded?
Spring doesn't differentiate packages from src/test/java or src/main/java for your component scan.
#ComponentScan(basePackages = {"br.com.example"})
is essentially scanning all the #Configurations,all packages within current package and sub-packages starting from "br.com.example".
These are the options available for you:
Change your package structure to give a more specific package to scan for test classes.
You can use the filters in #ComponenScan to include/exclude specific packages/classes. You can avoid the unwanted classes to be picked up by spring when loading the application context this way.
If its possible to handpick the required configurations to load the context for tests, you can even remove the #ComponentScan altogether and specify all the configuration classes in "classes" attribute.
You can use #Profile in your configuration classes and #ActiveProfiles in your test classes to map the configuration classes to be loaded in specific profiles.