Mock Spring Component

lives picture lives · Feb 5, 2017 · Viewed 10k times · Source

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.

Answer

René Scheibe picture René Scheibe · Feb 11, 2017

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