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.
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 { ... }