Disable Spring TestExecutionListeners from inherited class - spring

I have the following scenario:
#TestExecutionListeners(BasicListener.class)
public #interface AnnotationOne {
}
#AnnotationOne
public class TestClassOne extends AbstractJUnit4SpringContextTests {
...
}
And since:
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({ServletTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class})
public abstract class AbstractJUnit4SpringContextTests implements ApplicationContextAware {
...
}
For some reason, the TestExecutionListeners don't get merged. So, is there a way to disable the TestExecutionListeners defined by the parent class?
My main concern is to have the BasicListener working.
Thanks

The behavior you are experiencing is due to a bug in core Spring: an inherited annotation will shadow a locally declared composed annotation.
In your use case, #TestExecutionListeners is the inherited annotation, and #AnnotationOne is the locally declared composed annotation.
This bug has been fixed in Spring Framework 4.2 RC1. For details, see SPR-12749.
If you'd like a work-around before 4.2 is released, the following should work for you, but you will not be able to use your custom #AnnotationOne annotation if your class extends from AbstractJUnit4SpringContextTests:
#TestExecutionListeners(listeners = BasicListener.class, inheritListeners = false)
public class TestClassOne extends AbstractJUnit4SpringContextTests {
// ...
}
Generally speaking, AbstractJUnit4SpringContextTests adds very little value. So you're probably better off with the following approach:
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners(BasicListener.class)
public class TestClassOne {
// ...
}
Or even:
#RunWith(SpringJUnit4ClassRunner.class)
#AnnotationOne
public class TestClassOne {
// ...
}
Regards,
Sam (author of the Spring TestContext Framework)

Related

Why #WebMvcTest(MyCotroller.class) creates mongo repositories and in general boots complete application?

I have simple #RestController and I have tried to write slice test for it using #WebMvcTest like this
#RestController
public class ValidationController {
private final ValidationService validationService;
...}
#WebMvcTest(ValidationController.class)
#TestPropertySource(properties = {
"org.springframework.web=DEBUG"
})
public class ValidationControllerTest {
#MockBean
ValidationService validationService;
but for whatever resons, app context fails due to..... missing XService bean which is totally MVN unrelated compoennt and is not used in the controller.
I though that #WebMvcTest(ValidationController.class) will boot only web layer with single explicitly mentioned controller, but for whatever reasons, it works more like #SpringBootTest
What do work as I expect is this
#WebMvcTest
#ContextConfiguration(classes = ValidationController.class)
#TestPropertySource(properties = {
"org.springframework.web=DEBUG"
})
public class ValidationControllerTest extends AbstractControllerTest {
Why is that? Is this a bug or somehow expected behavior?
My expectations were to both snippets to work the same way. Here answer somehow confirms it https://stackoverflow.com/a/64923895/1527544 but I see a different behavior.

In SpringBoot configuring, how do the classes of #Import get turned into a globally accessible List of classes?

I have inherited some code that involves SpringBoot, #Import, and #Autowired. The code works, but I don't understand why. Can someone tell me what is going on?
In Spring Boot configuration code I have code much like this:
#Configuration
#Import({ FirstCombo.class, SecondCombo.class, ThirdCombo.class })
public class MyProgramConfiguration {
...
}
The (FirstCombo, SecondCombo, ThirdCombo) all extend the Combo class.
Then there is a business logic class that has this:
public class MyBusinessLogic {
#Autowired private List<Combo> combos;
public void doSomething() {
for(Combo c: combos) {
...
}
}
}
The method MyBusinessLogic has access to the List containing the imported Combo classes. It works in the real world. My best guess is that when #Import does its work it creates a globally available List<Combo> instance. But I'm unable to find documentation that this is what is really happening.
Can someone tell me what is really going on with this sort of code?
Thanks,
Jerome.
It's here
https://docs.spring.io/spring-framework/docs/2.5.x/reference/beans.html#beans-autowired-annotation
It is also possible to provide all beans of a particular type from the
ApplicationContext by adding the annotation to a field or method that
expects an array of that type:
public class MovieRecommender {
#Autowired
private MovieCatalog[] movieCatalogs;
// ... } The same applies for typed collections:
public class MovieRecommender {
private Set<MovieCatalog> movieCatalogs;
#Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ... }
You can try also with
#Autowired
private List<Object> allObjects;
To fetch all available beans in your class.

Spring boot #Inject proxy resolves to null

I'm refactoring an existing application to use Spring Boot. The issues I've faced here are generally of the type "why is this not working anymore".
I have three packages
- nl.myproject.boot
- nl.myproject
- nl.myproject.rest
My current problem is that all #Services that I #Inject in a #RESTController resolve to null when a method is called on them.
The service and dao are part of the nl.myproject package and the reason it's not nl.myproject.core is a legacy issue.
A related issue is that my #Configuration components don't seem to be loaded through #ComponentScan and I have to import them manually. I also had to exclude Test configuration to prevent Test configs from being loaded, which also seemed weird.
Internal calls from the service layer during start up, such as data preparation works normally. Any such manager is also #Injected. This is just to say that any of the typical injection mistakes such as manual instantiation or injecting a class instead of an interface don't apply.
I'd also be grateful for debugging tips. My Java has gotten a little rusty.
#EnableAutoConfiguration
#ComponentScan(basePackages= {
"nl.myproject",
"nl.myproject.boot",
"nl.myproject.dao",
"nl.myproject.service",
"nl.myproject.webapp"},
excludeFilters= {
#ComponentScan.Filter(type=FilterType.REGEX,pattern={".*Test.*"}),
#ComponentScan.Filter(type=FilterType.REGEX,pattern={".*AppConfig"})
}
)
#Configuration
#EnableConfigurationProperties
#Import({
JPAConfig.class,
RestConfig.class,
BootConfig.class
})
public class Startup {
public static void main(String[] args) throws Exception {
SpringApplication.run(Startup.class, args);
}
}
#RestController
#RequestMapping(value="/json/tags")
public class JsonTagController extends JsonBaseController {
#Inject
TagManager tagMgr;
public interface TagManager extends BaseManager<Tag,Long> {
[...]
}
#Service("tagManager")
public class TagManagerImpl extends BaseManagerImpl<Tag, Long> implements
TagManager {
#Inject
TagDao dao;
[...]
#Inject is a annotation specified by JSR-330 (standard) whereas #Autowired is annotation specified by Spring.
They just do the same dependency injection. You can both of them in the same code.
Just the modification (separation of the concerns) you need :
public interface TagManager {
[...]
}
#Service
public class TagManagerImpl implements TagManager {
#Inject
private TagDao dao;
// inject that service rather than extending
#Inject
private BaseManager<Tag,Long> baseManager;
}
public interface BaseManager<Tag,Long> {
[...]
}
#Service
public class BaseManagerImpl<Tag,Long> implements BaseManager<Tag,Long> {
....
}
Just one thing you do for checking, just modify to basePackages= {"nl.myproject"} - just provide only base package, that's enough for spring to scan the components in every package.
Hope this may help :)

BeanNotOfRequiredTypeException in Spring boot test runner and #Rule

I am writing an integration test using spring runner, and have created a TestRule implementation and used it through #Rule. But I try to create a bean of that implementation, I get BeanNotOfRequiredTypeException.
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'localDynamoDB' is expected to be of type 'com.wickes.dynamo.local.LocalDynamodb' but was actually of type 'com.sun.proxy.$Proxy114'
My test class is:
#ComponentScan(basePackages = "com.wickes.stock")
#Configuration
public class TestConfig {
#Bean
public LocalDynamodb localDynamoDB() {
return new LocalDynamodb();
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestConfig.class)
public class StockListenerTest {
#Rule
#Autowired
public LocalDynamodb localDynamodb;
#Test
public void test() {
}
}
and my config is:
My LocalDynamodb is
public class LocalDynamodb extends ExternalResource {
}
M. Deinum is correct: LocalDynamodb is being proxied by interfaces, which you do not want.
Thus, you have two options:
Convert LocalDynamodb to an interface, implement the interface, and register the implementation as the bean.
Switch from dynamic interface-based proxies (the default) to class-based proxies. How you perform the switch depends on how the proxies are being created, but since we can't see the rest of your Spring configuration we don't know how to advise you there.
Regards,
Sam (author of the Spring TestContext Framework)
I had a similar problem when trying to inject an AspectJ proxied object, then I found that you can add the #Scope annotation to LocalDynamodb:
#Component
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ...
More details here

Can #ContextConfiguration in a custom annotation be merged?

I am working on custom Spring Boot starters. In a test starter what I wanted do to is to implement a composed annotation, which would add additional #Configuration classes to the ApplicationContext (and possibly use this annotation in a TestExecutionListener). ex:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#ContextConfiguration(classes = AdditionalTestConfiguration.class)
public #interface ComposedAnnotation {
}
And use that in my Spring Boot integration test:
#RunWith(SpringJUnit4ClassRunner.class)
#WebIntegrationTest
#SpringApplicationConfiguration(Application.class)
#ComposedAnnotation
public class SomeTest {
}
No inheritance is involved. Unfortunately, it does not seem to work. I doubt it's a Spring Boot thing, rather Spring testing framework itself.
Is there any way I can achieve expected result?
You're right: this is not an issue with Spring Boot. But it's also not an issue with spring-test.
Rather, it's the intended behavior of Spring in general. For details, check out my answer to this question: #ActiveProfiles in meta annotation and on test class not working
In summary, you cannot achieve this with two #ContextConfiguration annotations declared on an individual test class (either directly or as meta-annotations).
However, I just came up with a trick that will allow you to achieve this. Specifically, you can create an ApplicationContextInitializer (ACI) that registers one or more #Configuration classes. In your composed annotation, you can then register this ACI to register the always present #Configuration classes. And when the composed annotation is actually used, it can declare additional #Configuration classes like normal.
I just submitted a working example in this commit.
Basically, the code would look something like this:
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, initializers = FooConfigInitializer.class)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface ComposedContextConfiguration {
#AliasFor(annotation = ContextConfiguration.class, attribute = "classes")
Class<?>[] value() default {};
}
public class FooConfigInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
#Override
public void initialize(GenericApplicationContext applicationContext) {
new AnnotatedBeanDefinitionReader(applicationContext).register(FooConfig.class);
}
}
And you can use it like this:
#RunWith(SpringRunner.class)
#ComposedContextConfiguration(BarConfig.class)
public class InitializerConfiguredViaMetaAnnotationTests { /* ... */ }
Your ApplicationContext will then be loaded from FooConfig and BarConfig.
The above examples obviously do not use Spring Boot, but the same principles should also be applicable to #SpringApplicationConfiguration.
Regards,
Sam (author of the Spring TestContext Framework)

Resources