How to get Roo integration tests to work with AnnotationConfigContextLoader - java

I have been running roo generated integration tests for quite a while with XML-based spring configuration and spring 3.1. However, I have recently changed from XML-based configuration to java based configuation and also upgraded from Spring 3.1 to Spring 4.0.
Unfortunately, my roo genenerated integration tests now fail with the exception:
java.lang.IllegalStateException: Test class [com.bulb.learn.domain.CounterIntegrationTest] has been configured with #ContextConfiguration's 'locations' (or 'value') attribute {classpath*:/META-INF/spring/applicationContext*.xml}, but AnnotationConfigContextLoader does not support resource locations.
at org.springframework.test.context.support.AnnotationConfigContextLoader.validateMergedContextConfiguration(AnnotationConfigContextLoader.java:164)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:111)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContextInternal(CacheAwareContextLoaderDelegate.java:64)
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:91)
... 32 more
I know this is being caused by this line in the roo generated aspect file:
declare #type: CounterIntegrationTest: #ContextConfiguration(locations = "classpath*:/META-INF/spring/applicationContext*.xml");
That is because I use this for my main test class base class:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles(profiles = "junit")
#ContextConfiguration(classes = { CoreConfig.class }, loader = AnnotationConfigContextLoader.class)
public abstract class CoreIntegrationTest {
...
}
Here is how I define the class that is annotated by #RooIntegrationTest
#RooIntegrationTest(entity = Counter.class, transactional = false)
public class CounterIntegrationTest extends CoreIntegrationTest {
However, I am not sure how to fix it.
The roo tests used to run fine. They also run fine with just upgrading to Spring 4.0. They also run fine with just changing to java configuration for Spring 3.1. However, they don't work with both (Spring 4.0 with Java config)
Does anyone have any ideas how I can get these to work without simply removing them from roo control and removing the offending line myself?
Thanks in advance!

Turns out that Roo looks at the annotated class to see if it already has a #ContextConfiguration before it adds one to the aspect. Unfortunately, it doesn't check to see if it has inherited a #ContextConfiguration.
I was able to solve the issue by adding a default #ContextConfiguration to the annotated class like this:
#ContextConfiguration
#RooIntegrationTest(entity = Counter.class, transactional = false)
public class CounterIntegrationTest extends CoreIntegrationTest {
This prevents Roo from generating its own, and therefore the error goes away.

Related

Trouble executing a unit test that should ignore Spring annotations on the unit under test

I'm trying to execute a unit test for a service class that has an #Async("asyncExecutor") annotated method. This is a plain JUnit test class with no Spring runners and no intention of using Spring at all in the unit test. I get the exception,
BeanFactory must be set on AnnotationAsyncExecutionAspect to access qualified executor 'asyncExecutor'
Where asyncExectuor is the name of the bean to be used during normal execution. My configuration class looks like this and I solved that previous error message at runtime by adding the mode = AdviceMode.ASPECTJ portion. This service works at runtime without issue in an Async way.
#Configuration
#EnableAsync(mode = AdviceMode.ASPECTJ)
public class AsyncConfiguration {
#Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
...
}
}
I don't understand why the Spring context is being constructed at all in the unit test. The test class is simply annotated #Test on the methods with no class annotations and no mention of Spring. I was hoping to unit test this service class method as a regular method ignoring the async nature, but the annotation is being processed for some reason.
I'm contributing to a much larger gradle + Spring 4 project that I'm not fully knowledgeable about. Is there anything I should be looking for to see if a Spring context is being created by default for all tests?
As you noticed, Spring context is not loaded, that is the reason of your error. Try to initialize Spring context in your test by adding #RunWith and #ContextConfiguration annotations

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

LoadTimeWeaving in Spring Boot 1.4.3+ not working after upgrade from 1.4.2

I have a working Spring Boot project with LoadTimeWeaving enabled. When I tell Gradle to use Spring Boot 1.4.3 (or higher) instead of 1.4.2 the application can no longer start, giving an error of the form:
Error starting ApplicationContext. [...]
Caused by: java.lang.NoSuchMethodError: [path.to.entity.or.entity.super.class]._persistence_set(Ljava/lang/String;Ljava/lang/Object;)V
_persistence_set can also be _init() or something else.
The exception, to my understanding, just means that the entity (or the super class of an entity) was not properly woven and hence the methods that should be woven into the class cannot be called (like _persistence_set(), _init() etc).
I used the argument -verbose:class to start my project with, which then prints every class when it is loaded. Turns out with Spring Boot 1.4.2 (where everything is working) the entities are loaded after the LoadTimeWeaver is initialized, while with Spring Boot 1.4.3+ multiple abstract base entity classes are loaded before that point. This means that they are not woven when they are loaded as the weaver isn't initialized yet, but they will not be woven after weaver-initialization because they only get loaded once.
Now its not obvious why these base entity classes are suddenly loaded before the weaver is initialized. Ideas?
I tried to solve this for days. This is what I found after debugging how spring initializes the application context (and with that the beans, weaver etc).
We had some #Configuration classes that were defining #Bean that were typed with entities. Like this:
#Configuration
public class UserModuleConfiguration {
#Bean
public BasePresenter<EUser> userPresenter() {
return new BasePresenter<EUser>() {
};
}
}
When I removed the entity from the return type of that method, everything worked. Note that since there is more than one of these configurations providing BasePresenter for different entities, you have to use #Qualifier or just instantiate them in their own right. Something like this:
#Configuration
public class UserModuleConfiguration {
#Bean("userPresenter")
public BasePresenter<?> userPresenter() {
return new BasePresenter<EUser>() {
};
}
}
or
#Component
public class UserPresenter<EUser> extends BasePresenter{
}
Internally, before starting to 'evaluate' configurations spring is determining what beans are available to determine if specific configurations have to be loaded or not (see #ConditionalOnMissingBean and the like) and apparently in the process of doing this spring loads some of the super classes of the entities. I just wanted to leave this here, because I really did not find anything about this being a possible cause and it took me ages to find it.

ContextConfiguration annotation exception

I have a pretty simple Spring Boot project that was just upgraded from Spring Boot 1.2.5.RELEASE to Spring Boot 1.3.0.M5 (which then relies on Spring 4.2.0.RELEASE), and now my project won't compile.
Project:
#Configuration
#ComponentScan
#EnableAutoConfiguration
#EnableEncryptableProperties
public class MyApp extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
Test that fails compilation (my only test ATM):
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {MyApp.class})
#DirtiesContext
public class MyDAO_DataTest {
#Autowired
MyDAO dao;
#Test
public void whenDoingAtest() throws Exception {
//...
}
}
When I try to compile, it fails on my test file, saying:
org.springframework.core.annotation.AnnotationConfigurationException: In AnnotationAttributes for annotation [org.springframework.test.context.ContextConfiguration] declared on [class com.example.MyDAO_DataTest], attribute [locations] and its alias [value] are declared with values of [{}] and [{class com.example.MyApp}], but only one declaration is permitted.
I found the feature that's the origin of the exception, but I don't really understand what I can do about it.
Update I "fixed" the issue by changing this line:
#SpringApplicationConfiguration(classes = {MyApp.class})
... to this:
#ContextConfiguration(classes = {MyApp.class},
loader = SpringApplicationContextLoader.class)
effectively working around the issue and allowing myself to work, but I don't understand why I had to. #SpringApplicationConfiguration is described as Similar to the standard ContextConfiguration but uses Spring Boot's SpringApplicationContextLoader, so what's the deal?
Spring Boot 1.3.0.M5 (which then relies on Spring 4.2.0.RELEASE)
That is unfortunately incorrect: Spring Boot 1.3.0.M5 depends explicitly on Spring Framework 4.2.1, not 4.2.0.
The exception you are seeing was addressed in Spring Framework 4.2.1, specifically in the following issues.
https://jira.spring.io/browse/SPR-13325
https://jira.spring.io/browse/SPR-13345
And changes made to #SpringApplicationConfiguration in Spring Boot 1.3.0 M5 require Spring Framework 4.2.1. See the following issue for details.
https://github.com/spring-projects/spring-boot/issues/3635
Thus, ensuring that you are running against Spring Framework 4.2.1 should resolve your problem.
Regards,
Sam

Spring autowiring dependencies after each junit test

I'm having a problem whereby spring is re-creating my beans and re-auto wiring after each junit test has been run. I only want spring to do this once, on the post construct of the test class, which is what I've configured it to do.
I've also tried setting the #DirtiesContext class mode to AFTER_CLASS, but this still didn't solve the issue.
Any ideas?
Cheers.
You can create a class:
#SpringJUnitConfig(classes = {ConfigClassOne.class, ConfigClassTwo.class, ConfigClassThree.class})
public abstract class ModelTest {}
and extend it in the test classes. Beans will not be recreated because the application contexts are already mentioned in the annotation.

Categories

Resources