SpringExtension without an explicit configuration? - java

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

Related

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)

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.

Spring boot test configuration not being picked

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.

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());
}
}

Should I annotate configuration class as #Configuration for testing?

I spent some time resolving problem with missing org.joda.time.DateTime->java.util.Date converter in Spring Data (which should be enabled by default when Joda-Time is on a classpath). I have found a reason, but it generated a question about #Configuration annotation in Spring.
Standard application config using AbstractMongoConfiguration from spring-data-mongodb:
#Configuration
#ComponentScan
#EnableMongoRepositories
public class AppConfig extends AbstractMongoConfiguration { ... }
A test which explicit uses AppConfig class (with Spock, but internally mechanisms provided by spring-test are used):
#ContextConfiguration(classes = AppConfig)
class JodaDocRepositorySpec extends Specification {
#Autowired
private JodaDocRepository jodaDocRepository
def "save document with DateTime"() {
given:
def jodaDoc = new JodaDoc(DateTime.now())
when:
def savedJodaDoc = jodaDocRepository.save(jodaDoc)
then:
savedJodaDoc.id
}
}
It works fine. But when #Configuration annotation in AppConfig is removed/commented:
//#Configuration
#ComponentScan
#EnableMongoRepositories
public class AppConfig extends AbstractMongoConfiguration { ... }
the test fails with:
org.springframework.core.convert.ConverterNotFoundException:
No converter found capable of converting from type org.joda.time.DateTime to type java.util.Date
AFAIK it is not needed to use #Configuration for the configuration class when it is explicit registered in the context (by classes in #ContextConfiguration or a register() method in AnnotationConfigWebApplicationContext). The classes are processed anyway and all declared beans are found. It is sometimes useful to not use #Configuration to prevent detecting by a component scan when there are 2 similar configuration classes in the same packages in a test context used by different tests.
Therefor I think it could a bug in Spring which causes to different internal beans processing in the context depending on an usage or not a #Configuration annotation. I compared Spring logs from these two cases and there are some differences, but I'm not able to determine what are they caused by in the Spring internal classes. Before a bug submission I would like to ask:
My question. Is there an explicable reason why Spring for the same configuration class (pointed explicit in #ContextConfiguration) uses (or not) converters for Joda-Time depending on an existence of a #Configuration annotation?
I created also a quickstart project reproducing the issue. spring-data-mongodb 1.3.3, spring 4.0.0, joda-time 2.3.
It's everything OK in this behaviour. AbstractMongoConfiguration is annotated by #Configuration, but in fact this annotation is not #Inherited, so you have to explicitly annotate your class.
When you remove #Configuration annotation then your AppConfig class is not a full configuration. It's processes as a lite configuration just because it contains methods annotated by #Bean - please refer to methods in org.springframework.context.annotation.ConfigurationClassUtils
isFullConfigurationCandidate()
isLiteConfigurationCandidate()
isFullConfigurationClass()
Finally only full (annotated by #Configuration) configuration classes are processes and enhanced by configuration post processors - look at ConfigurationClassPostProcessor.enhanceConfigurationClasses()

Categories

Resources