Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock Spring Component

Tags:

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.

like image 730
lives Avatar asked Feb 05 '17 07:02

lives


People also ask

How do you mock a component in spring boot?

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.

What is difference between @mock and @injectmock?

@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.

How do you test a spring boot component?

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.

What is the difference between @mock and @MockBean?

@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.


1 Answers

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 { ... }
like image 80
René Scheibe Avatar answered Sep 23 '22 10:09

René Scheibe