I am using Mockito to mock spring beans.
It works fine when I mock an interface.
In our application, there are few @Component beans which do not implement any interface.
When I try to mock such component , the spring context tries to inject the properties inside those components.
Does Mockito not support mocking spring components which do not implement any interface ?
Attached example as requested
public interface EmployeeInterface {
public Long saveEmployee(Employee employee);
}
@Component
public class EmployeeImpl implements EmployeeInterface {
@Autowired
public EmailSender emailSender
public Long saveEmployee(Employee employee) {
...
}
}
public interface EmailSender {
public boolean sendEmail(Email email);
}
@Component
public class EmailSenderImpl implements EmailSender {
@Autowired
MailServerInfo MailServerInfo;
public boolean sendEmail(Email email) {
...
}
}
public interface MailServerInfo {
public String getMailServerDetails();
}
@Component
public class MailServerInfoImpl {
public String getMailServerDetails() {
...
}
}
@Profile("Security-test")
@Configuration
public class SecurityTestMockConfiguration {
@Bean
@Primary
public EmailSender emailSender() {
return Mockito.mock(EmailSender.class);
}
}
@ActiveProfiles("Security-test")
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:context-test.xml" })
public class MyTest {
@Autowired
EmployeeInterface employeeInterface;
@Test
public void testSaveEmployee() {
employeeInterface.saveEmployee(employee);
}
}
In the above example if I mock EmailSender using Mockito it works perfectly fine.
In the below scenario, EmailSender is a Spring component which does not implement any interface. In the below case, I get error during auto wiring.
public interface EmployeeInterface {
public Long saveEmployee(Employee employee);
}
@Component
public class EmployeeImpl implements EmployeeInterface {
@Autowired
public EmailSender emailSender
public Long saveEmployee(Employee employee) {
...
}
}
@Component
public class EmailSender {
@Autowired
MailServerInfo MailServerInfo;
public boolean sendEmail(Email email) {
...
}
}
public interface MailServerInfo {
public String getMailServerDetails();
}
@Component
public class MailServerInfoImpl {
public String getMailServerDetails() {
...
}
}
@Profile("Security-test")
@Configuration
public class SecurityTestMockConfiguration {
@Bean
@Primary
public EmailSender emailSender() {
return Mockito.mock(EmailSender.class);
}
}
@ActiveProfiles("Security-test")
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:context-test.xml" })
public class MyTest {
@Autowired
EmployeeInterface employeeInterface;
@Test
public void testSaveEmployee() {
employeeInterface.saveEmployee(employee);
}
}
In the second scenario, the autowiring fails because EmailSender could not find MailServerInfo implementation.
Spring Boot's @MockBean Annotation We can use the @MockBean to add mock objects to the Spring application context. The mock will replace any existing bean of the same type in the application context. If no bean of the same type is defined, a new one will be added.
@Mock is used to create mocks that are needed to support the testing of the class to be tested. @InjectMocks is used to create class instances that need to be tested in the test class. Annotated class to be tested dependencies with @Mock annotation.
To achieve this, we can use the mocking support provided by Spring Boot Test. To check the Service class, we need to have an instance of the Service class created and available as a @Bean so that we can @Autowire it in our test class. We can achieve this configuration using the @TestConfiguration annotation.
@Mock is used when the application context is not up and you need to Mock a service/Bean. @MockBean is used when the application context(in terms of testing) is up and you need to mock a service/Bean.
Problems
You are mixing frameworks and annotations quite a bit. Spring uses @Autowired
. Mockito uses @Mock
and @InjectMocks
. You also use multiple ways to configure your application context in the tests - @Configuration
and @ContextConfiguration(locations = { ... })
- which is not working this way. See Mixing XML, Groovy scripts, and annotated classes for all the details.
The question is if you want to write a unit test using mocks or if you want to write an integration test using no mocks but a Spring context?
Unit Tests
If you want to mock EmailSender
then use Mockito's @Mock
annotation. You can then let Mockito inject the dependencies of EmployeeImpl
via @InjectMocks
.
@Mock
EmailSender emailSender;
@InjectMocks
EmployeeImpl employee;
You can also have a look at a tutorial like this https://dzone.com/articles/use-mockito-mock-autowired
Integration Tests
If you want to write an integration test use either
@Configuration
public class TestConfiguration { ... }
@ContextConfiguration(classes = { TestConfiguration.class })
public class MyTest { ... }
or
@ContextConfiguration(locations = { "classpath:context-test.xml" })
public class MyTest { ... }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With