#Autowired bean is null in Test Listener class - spring

This question was asked before Using Autowired in a TestExecutionListener class for BeforeClass junit however it wasn't answered. I am facing the same problem but haven't figured out the solution
Example: I am getting null mapper.
public class CustomExecutionListener extends AbstractTestExecutionListener {
#Autowired
private Mapper mapper;
#Override
public void beforeTestClass(TestContext testContext) {}
... some code...
}
Test Class: Note: AppConfig contains the Mapper Bean defined.
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners(listeners = {DependencyInjectionTestExecutionListener.class, CustomExecutionListener.class})
#ContextConfiguration(classes = {AppConfig.class})
public class AccountControllerTest {
....
}

Dependency injection is not supported for TestExecutionListener instances.
Dependency injection is only supported for test instances.
Thus, if your CustomExecutionListener needs to access a bean from the ApplicationContext, it will have to look it up manually -- for example, like this:
public void beforeTestClass(TestContext testContext) {
Mapper mapper = testContext.getApplicationContext().getBean(Mapper.class);
// ... some code...
}
Regards,
Sam (author of the Spring TestContext Framework)

You can also try this: Mapper mapper = Mappers.getMapper(Mapper.class);

Related

How to avoid a second Instantiation of a spring bean in child test context

I created an Embedded Sftp server bean for my integration tests, i hooked the startup and the shutdown of the server respectively with the afterPropertiesSet and destroy life cycles
public class EmbeddedSftpServer implements InitializingBean, DisposableBean {
//other class content
#Override
public void afterPropertiesSet() throws Exception {
//Code for starting server here
}
#Override
public void destroy() throws Exception {
//Code for stopping server here
}
}
here my config class
#TestConfiguration
public class SftpTestConfig {
#Bean
public EmbeddedSftpServer embeddedSftpServer() {
return new EmbeddedSftpServer();
}
//Other bean definitions
}
Now when i inject the bean in my test classes like the following :
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = SftpTestConfig .class)
class ExampleOneIT {
#Autowired
private EmbeddedSftpServer embeddedSftpServer;
}
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = SftpTestConfig .class)
class ExampleTwoIT {
#Autowired
private EmbeddedSftpServer embeddedSftpServer;
}
#SpringBatchTest
#ContextConfiguration(classes = SftpTestConfig .class)
class ExampleThreeIT {
#Autowired
private EmbeddedSftpServer embeddedSftpServer;
}
And i run all the test classes simultaneously, i found out that for the test classes annotated with #ExtendWith(SpringExtension.class), it's the same context that is used (which is understandable since i guess spring cache it) and therefore the bean lifecycle methods are not executed again, but to my surprise, for the class annotated with #SpringBatchTest i noticed that the life cycle hooks of the bean are executed again! Which is a behavior that is not convenient since i want the application context to start the server one time for all tests and close it at the end of those tests (which is the case if i use only #ExtendWith(SpringExtension.class) for all my test classes).
N.B. : I need to use #SpringBachTest for my ExampleThreeIT test class.
I think you are hitting this issue: https://github.com/spring-projects/spring-batch/issues/3940 which has been fixed in v4.3.4/4.2.8. Upgrading to one of these versions should fix your issue.

Is there a way to include a spring component in a WebMvcTest

Given production code classes:
#RestController
#RequiredArgsConstructor
public class MyController {
private final MyValidator validator;
// annotations relating to request mapping excluded for brevity
public void test(#Valid #RequestBody final MyParams params) {
// do stuff
}
#InitBinder
#SuppressWarnings("unused")
protected void initBinder(final WebDataBinder binder) {
binder.setValidator(validator);
}
}
and
#Component
#RequiredArgsConstructor
public class MyValidator implements Validator {
...
#Override
public void validate(final Object target, final Errors errors) {
// custom validation
}
}
and finally test code:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
// tests
}
I encounter the error:
NoSuchBeanDefinitionException: No qualifying bean of type 'MyValidator' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
I think the error is fair enough. I've annotated the test as a WebMvcTest, which I believe has excluded #Component beans. This is intentional and desired (from the perspective that I am only wanting to test the "web layer", not the whole context - it just so happens I need a component which is related/used only in the controllers)
My question, therefore, is: how can one explicitly include a component like a validator in the test context for a web test?
My environment is java version "10.0.2" 2018-07-17, spring boot 1.5.16.RELEASE.
There are two ways to solve this.
Using #SpringBootTest and #AutoConfigureMvc instead of #RunWith(SpringRunner.class) and #WebMvcTest.
#SpringBootTest
#AutoConfigureMvc
public class MyControllerTest {
}
Creating a #TestConfiguration class that injects the 'MyValidator' bean as:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
#TestConfiguration
static class TestConfig {
#Bean
MyValidator getMyValidator(){
return new MyValidator();
}
}
// tests
}
More on this can be found here : https://mkyong.com/spring-boot/spring-boot-how-to-init-a-bean-for-testing/
There are two ways to test the web layer
first.
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyControllerTest {
#Autowired
private MyController myController;
}
The #SpringBootTest annotation tells Spring Boot to go and look for a
main configuration class (one with #SpringBootApplication for
instance), and use that to start a Spring application context.
A nice feature of the Spring Test support is that the application
context is cached in between tests, so if you have multiple methods in
a test case, or multiple test cases with the same configuration, they
only incur the cost of starting the application once. You can control
the cache using the #DirtiesContext annotation.
Secondly, if you want to use the #WebMvcTest(MyController.class)
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
#MockBean
private MyValidator validator;
}
But this validator is a fake, so you have to customize it for testing.
See this link for more details https://spring.io/guides/gs/testing-web/
I cannot recommend it as a standard practice but if you do need an instance of a dependency in your Web MVC tests (for example in legacy code) you can add them into the spring context using #SpyBean annotation.
Real methods of that class will be called during the test and you can verify them if needed similarly to the beans annotated with #MockBean
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
#SpyBean
private MyValidator validator
}

mock beans inside a method and those beans are also autowired in the same class or super class

#RunWith(SpringRunner.class)
#WebAppConfiguration
Class Test{
#Autowired
public SomeBean someBean;
#Test
public void testAccountPage2() throws Exception {
SomeBean someBean = mock(SomeBean.class);
given(someBean.getAccount(anyString())).willReturn(getCustomer());
}
Here someBean.getAccount(anyString()) is not mocking, which is calling the actual method of that bean impl. It seems it's taking the Autowired object not the mocked one.
Could anyone help me to mock beans in method level? those beans are also autowired in the same class or superclass.
Thanks
To replace a bean in the Spring container by a Mockito mock, use #MockBean.
import org.springframework.boot.test.mock.mockito.MockBean; // import to add
#RunWith(SpringRunner.class)
#WebAppConfiguration
Class Test{
#MockBean
public SomeBean someBean;
#Test
public void testAccountPage2() throws Exception {
given(someBean.getAccount(anyString())).willReturn(getCustomer());
}
}
To understand the difference between Mockito and MockBean from Spring Boot, you can refer to this question.
You need to inject the mock in order to have it working instead of autowiring
//if you are just testing bean/services normally you do not need the whole application context
#RunWith(MockitoJUnitRunner.class)
public class UnitTestExample {
#InjectMocks
private SomeBean someBean = new SomeBean();
#Test
public void sampleTest() throws Exception {
//GIVEN
given(
someBean.getAccount(
//you should add the proper expected parameter
any()
)).willReturn(
//you should add the proper answer, let's assume it is an Account class
new Customer()
);
//DO
//TODO invoke the service/method that use the getAccount of SomeBean
Account result = someBean.getAccount("");
//VERIFY
assertThat(result).isNotNull();
//...add your meaningful checks
}
}

Spring Boot Custom AutoConfiguration and Autowire

I am creating a custom AutoConfiguration for Spring Boot. One of the features I was attempting to create was to create one or more Beans dynamically and adding them to the ApplicationContext at runtime.
The problem I ran into was with Autowiring. My #SpringBootApplication class autowires those beans, and since they do not exist yet, autowire fails.
My first solution was to put #Lazy on the autowire, and that solved my problem.
However, I ran into something interesting. I added two beans that I was looking for into the AutoConfiguration code, and of course, it worked. By accident, I only removed one of the beans and re-ran my code. It worked.
#SpringBootApplication
public class SpringBootDemoApplication {
#Autowired
#Qualifier("some_name")
private MyClass myClass;
#Autowired
#Qualifier("another_name")
private MyClass anotherClass;
...
}
#Configuration
public class MyAutoConfigurationClass {
#Bean(name="some_class")
public MyClass myClass () {
return null;
}
}
So the short of it is this. If I defined only one of the beans in my autoconfiguration class, this seems to satisfy Autowired and it does not blow up and when I dynamically add my other beans, both beans are found.
The stipulation is that the Autowired bean that is first, must be the bean that is defined in my autoconfiguration class.
I am running the following:
Spring Boot Starter 1.5.7-RELEASE
Various Spring Framework 4.3.11-RELEASE
Is this a bug? Or is this the way Autowired is supposed to work?
#SpringBootApplication
public class SpringBootDemoApplication {
#Autowired
#Qualifier("myclass")
private MyClass myClass;
#Autowired
#Qualifier("anotherMyClass")
private MyClass anotherMyClass;
...
}
#Configuration
public class MyAutoConfiguration {
private ConfigurableApplicationContext applicationContext;
private final BeanFactory beanFactory;
#Autowired
private MyClassFactory myClassFactory;
public MyAutoConfiguration(ApplicationContext applicationContext, BeanFactory beanFactory) {
this.beanFactory = beanFactory;
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
#PostConstruct
public void init() throws IOException, SQLException {
this.myClassFactory.create(this.applicationContext);
}
// without this #Bean definition SpringBoot will recieve the following error and stop
// AnnotationConfigEmbeddedWebApplicationContext - Exception encountered during context initialization
#Bean(name="myClass")
public DataSource anyNameWillDoItDoesntMatter() {
return null;
};
}
#Component
class MyClassFactory {
public void create(ConfigurableApplicationContext applicationContext) {
applicationContext.getBeanFactory().registerSingleton(name, value);
}
}
So is this expected behavior of #Autowired?

Inject #PersistenceContext in test unit for spring

I have problem when injecting entityManager in my test class. I think that this is because i can't load my spring context in my test class.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'noteDeFraisDAO': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManagerFactory' available
this is my test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {NoteDeFraisTestConfig.class})
#Transactional
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class NoteDeFraisDAOIT
{
#Autowired
private NoteDeFraisDAO noteDeFraisDAO;
#Test
public void myTest()
{
}
}
And this is the configuration for the test I know that when I use new there will be no inject of entitiesManager define in the dao but I don't know what should I do.
#Configuration
public class NoteDeFraisTestConfig {
#Bean
NoteDeFraisDAO noteDeFraisDAO()
{
return new NoteDeFraisDAO();
}
}
I have tried to set my ContextConfiguration to my applicationContext but it didn't work I think that it is because the directory WEB-INF doesn't belong to the classpath. How can I fix this??
This is the structure of my project
thanks.
Update
this is my final test class
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"file:web/WEB-INF/applicationContext.xml", "file:web/WEB-INF/db-config.xml","file:web/WEB-INF/dispatcher-servlet.xml","file:web/WEB-INF/security-config.xml"})
#Transactional
public class NoteDeFraisDAOIT
{
#Autowired
private NoteDeFraisDAO noteDeFraisDAO;
#Test
public void myTest()
{
}
}
Yep, the problem is that the ApplicationContext loaded for your test does not contain the LocalContainerEntityManagerFactoryBean. Assuming that's declared properly in applicationContext.xml, the linked solution below will help.
I have tried to set my ContextConfiguration to my applicationContext but it didn't work I think that it is because the directory WEB-INF doesn't belong to the classpath. How can I fix this??
That's covered here: Location of spring-context.xml

Resources