6

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.

René Scheibe
  • 1,970
  • 2
  • 14
  • 20
lives
  • 1,243
  • 5
  • 25
  • 61

1 Answers1

5

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 { ... }
René Scheibe
  • 1,970
  • 2
  • 14
  • 20
  • thanks for the clarification. That was useful . I want to write an integration test for a spring context. There will be nested autowiring of several beans within the application context. By using the ActiveProfile and @primary annotation , I am able to successfully mock such beans within the application context. In my example , from the junit test class , I access only the EmployeeInterface , which internally has dependencies on other beans . I get error only when I try to mock a Spring Component , which does not implement an interface. Otherwise it works .Hope I have made my scenario clear. – lives Feb 11 '17 at 06:54
  • 1
    The ContextConfiguration tag does not work with MockitoJunitRunner. – Shadab Apr 18 '18 at 06:25