Spring-boot: Support @MockBean on test fields with TestNG

Created on 18 Dec 2016  路  8Comments  路  Source: spring-projects/spring-boot

Using @MockBean on test fields is a very succinct way to mock components.

At the moment, it only works with JUnit tests via SpringRunner.

It would be great if it also worked with TestNG via AbstractTestNGSpringContextTests.

Example

@SpringBootTest
public class TestNGMockBeanTest extends AbstractTestNGSpringContextTests {

    @MockBean
    DemoComponent demoComponent;

    @Test
    public void mockBeanShouldWork() {
        assertTrue(demoComponent != null, "demoComponent is null");
        assertTrue(new MockUtil().isMock(demoComponent), "demoComponent is not a mock");
    }

}

Desired behaviour: Test passes.

Current behaviour: "demoComponent is null" assertion triggers.

enhancement

Most helpful comment

@qerub I found another workaround. I changed WebMockTest which is in Testing the Web Layer like this:

@WebMvcTest(GreetingController.class)
@TestExecutionListeners(MockitoTestExecutionListener.class)
@Test
public class WebMockTestNg extends AbstractTestNGSpringContextTests {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private GreetingService service;

    @Test
    public void greetingShouldReturnMessageFromService() throws Exception {
        when(service.greet()).thenReturn("Hello Mock");
        this.mockMvc.perform(get("/greeting")).andDo(print()).andExpect(status().isOk())
                .andExpect(content().string(containsString("Hello Mock")));
    }
}

or like below:

@SpringBootTest(classes = GreetingController.class)
@AutoConfigureWebMvc
@AutoConfigureMockMvc
@TestExecutionListeners(MockitoTestExecutionListener.class)
public class WebMockTestNg extends AbstractTestNGSpringContextTests {
  // ...
}

The key is to add @TestExecutionListeners(MockitoTestExecutionListener.class).

All 8 comments

I have the same problem. Unfortunately adding a name attribute to annotation @MockBean as suggested in this blog post doesn't help.

@Krasnyanskiy: Consider using @MockBean like this instead as a workaround:

@SpringBootTest
public class TestNGMockBeanWorkaroundTest extends AbstractTestNGSpringContextTests {

    @TestConfiguration
    static class Conf {
        @MockBean
        DemoComponent demoComponent;
    }

    @Autowired
    DemoComponent demoComponent;

    @Test
    public void mockBeanShouldWork() {
        assertTrue(demoComponent != null, "demoComponent is null");
        assertTrue(new MockUtil().isMock(demoComponent), "demoComponent is not a mock");
    }

}

Well, it's a very weird workaround. In my case Spring is still cannot inject a bean.

there is another workaround : just add @Autowired with @MockBeans and also add a manual reset since the automatic reset does not work
So the following will work :

 @SpringBootTest(classes = AppConfig.class)
    public @Log class ServiceDrhImplTest_TestNG extends AbstractTestNGSpringContextTests {

        private @Autowired ServiceDrh serviceDrh;
        private @MockBean @Autowired EmployeDao employeDao;
        private @MockBean @Autowired SalaireDao salaireDao;

        @AfterMethod
        public void afterMethod() {
            Mockito.reset(employeDao, salaireDao);
        }

But i could not make @BeforeTest and @BeforeSuite work => in those cases I don't have any DI of spring beans

But i could not make @BeforeTest and @BeforeSuite work => in those cases I don't have any DI of spring beans

That's expected.

Look at the source code of AbstractTestNGSpringContextTests.

If you want to have DI _before_ the @BeforeMethod lifecycle callbacks, you will have to implement your own version of AbstractTestNGSpringContextTests.

@qerub I found another workaround. I changed WebMockTest which is in Testing the Web Layer like this:

@WebMvcTest(GreetingController.class)
@TestExecutionListeners(MockitoTestExecutionListener.class)
@Test
public class WebMockTestNg extends AbstractTestNGSpringContextTests {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private GreetingService service;

    @Test
    public void greetingShouldReturnMessageFromService() throws Exception {
        when(service.greet()).thenReturn("Hello Mock");
        this.mockMvc.perform(get("/greeting")).andDo(print()).andExpect(status().isOk())
                .andExpect(content().string(containsString("Hello Mock")));
    }
}

or like below:

@SpringBootTest(classes = GreetingController.class)
@AutoConfigureWebMvc
@AutoConfigureMockMvc
@TestExecutionListeners(MockitoTestExecutionListener.class)
public class WebMockTestNg extends AbstractTestNGSpringContextTests {
  // ...
}

The key is to add @TestExecutionListeners(MockitoTestExecutionListener.class).

I met this same issue when use @SpringBootTest + JUnit5 + Mockito.

I found the above two solutions both work, thank you for your solutions.

Solution 1: Use MockBean + Autowired

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public abstract class BaseTest extends AbstractTransactionalJUnit4SpringContextTests {
...

public HelloServiceTest extends BaseTest {
    @MockBean
    @Autowired
    private WorldService worldService;

    @Autowired
    private HelloService helloService;

    @Test
    public void testGetStrings() {
        Mockito.when(worldService.getWorlds("James")).thenReturn(Arrays.asList("Bob!!!!"));
        System.out.println(helloService.getStrings("James"));

Solution 2: Use MockBean + MockitoTestExecutionListener

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@TestExecutionListeners(MockitoTestExecutionListener.class)
public abstract class BaseTest extends AbstractTransactionalJUnit4SpringContextTests {
...

public HelloServiceTest extends BaseTest {
    @MockBean
    private WorldService worldService;

    @Autowired
    private HelloService helloService;

    @Test
    public void testGetStrings() {
        Mockito.when(worldService.getWorlds("James")).thenReturn(Arrays.asList("Bob!!!!"));
        System.out.println(helloService.getStrings("James"));

@uniquejava, if you're writing your tests using JUnit Jupiter (i.e., JUnit 5), you ideally should not be extending AbstractTransactionalJUnit4SpringContextTests since that...

  1. is designed for JUnit 4
  2. overrides the default set of TestExecutionListeners including any of those provided by Spring Boot Test such as the MockitoTestExecutionListener

What happens if you completely remove the @TestExecutionListeners declaration, annotate your base test class with @Transactional, and stop extending AbstractTransactionalJUnit4SpringContextTests?

Was this page helpful?
0 / 5 - 0 ratings