Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Mockito with TestNG

I took a working test written for JUnit using Mockito and tried to adapt it to work with TestNG but oddly using TestNG only one test will work.

I think it is somehow related to the resetting of the mocks but I have played around with trying to call Mockito.reset and using BeforeMethod and BeforeClass and different combinations but still can only get one test to pass.

What I need to do to get the test to work?

@BeforeClass
public void setUp() {       
    MockitoAnnotations.initMocks(this);                                
    mockMvc = MockMvcBuilders.standaloneSetup(calculatorController).build();
}

@AfterMethod
public void reset() {
    Mockito.reset(calculatorService);
}

@Test
public void addFunctionTest() throws Exception {             
    Assert.assertNotNull(calculatorController);
     Result expectedResult = new Result();
     expectedResult.setResult(10);

    when(calculatorService.add(anyInt(), anyInt())).thenReturn(expectedResult);                             

    mockMvc.perform(get("/calculator/add").accept(MediaType.APPLICATION_JSON_VALUE)
            .param("val1", "100")
            .param("val2", "100"))  
    .andExpect(content().contentType("application/json"))
    .andExpect(status().isOk())
    .andExpect(jsonPath("$.result", equalTo(10)));    

    verify(calculatorService, times(1)).add(anyInt(), anyInt());
}   

@Test
public void subtractFunctionTest() throws Exception {            
    Assert.assertNotNull(calculatorController);
    Result expectedResult = new Result();
    expectedResult.setResult(90);

    when(calculatorService.subtract(anyInt(), anyInt())).thenReturn(expectedResult);                                

    mockMvc.perform(get("/calculator/subtract").accept(MediaType.APPLICATION_JSON_VALUE)
    .param("val1", "100")
    .param("val2", "10"))  
    .andExpect(content().contentType("application/json"))
    .andExpect(status().isOk())
    .andExpect(jsonPath("$.result", equalTo(90)));    

    verify(calculatorService, times(1)).subtract(anyInt(), anyInt());
}

The second test always seems to fail on assertions that either content type is not set or the expected result is wrong.

It seems like the response for the first test is somehow being evaluated in the second test and so is obviously wrong!

I know the controller and service work as expected and the exact same tests running with jUnit actually work ok.

I have managed to get the tests to perform properly only when I do the following:

 @BeforeGroups("subtract")
 public void reset() {      
    Mockito.reset(calculatorService);
    mockMvc =       MockMvcBuilders.standaloneSetup(calculatorController).build();
 }

 @Test(groups = "subtract")
 public void subtractFunctionTest() throws Exception {    
    System.out.println("***** IN METHOD *****");
    Assert.assertNotNull(calculatorController);
    Result expectedResult = new Result();
    expectedResult.setResult(90);

    when(calculatorService.subtract(anyInt(), anyInt())).thenReturn(expectedResult);                                

    //Perform HTTP Get for the homepage
    mockMvc.perform(get("/calculator/subtract").accept(MediaType.APPLICATION_JSON_VALUE)
    .param("val1", "100")
    .param("val2", "10"))  
    .andExpect(content().contentType("application/json"))
    .andExpect(status().isOk())
    .andExpect(jsonPath("$.result", equalTo(90)));    

    //Verify that the service method was only called one time
    verify(calculatorService, times(1)).subtract(anyInt(), anyInt());
}

This means I need to add one of these reset methods for each test method though and I then need a group per test method which doesnt seem correct.

like image 715
berimbolo Avatar asked Jun 02 '16 12:06

berimbolo


People also ask

Can we use Mockito with TestNG?

Mockito is a java based mocking framework, used in conjunction with other testing frameworks such as JUnit and TestNG. It internally uses Java Reflection API and allows to create objects of a service.

Is Mockito TDD or BDD?

Mockito uses the BDDMockito class that is available in the org. mockito package. It develops a test in BDD style. The BDD style of writing texts uses the //given //when //then comments as the primary part of the test body.

Is Mockito used for unit testing?

Mockito is a mocking framework, JAVA-based library that is used for effective unit testing of JAVA applications.


3 Answers

There is a difference in the behaviour of these frameworks:

  • JUnit creates a new instance of class for every of its test methods. This means that the fields are not shared between tests.
  • But TestNG creates only one object and thus the state in fields is shared between to @Tests

For Mockito you need to init mocks before every test method so that the state is not shared between two @Tests in TestNG:

@BeforeMethod
public void init() {
    MockitoAnnotations.initMocks(this);
}

For JUnit it works out of box because 2nd @Test has its own fields and its own mocks.

like image 145
Stanislav Bashkyrtsev Avatar answered Sep 22 '22 22:09

Stanislav Bashkyrtsev


I'm not sure, if this answer fits to the questioners problem, because the mocks are not listed. But I'd like to re-state one comment from andy in the accepted answer, which helped me out on the same problem. Here are some more details and an example:


public class B {

private A a;

B (A a) {
    this.a = a
}

// ...

import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;


public class MyTest {

    @Mock
    A a;

    @InjectMocks
    B B;

    @BeforeMethod
    public void init() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    void test1() {
        // ...
        // b.toString();
        // b.getA().toString();
        // a.toString();
    }

    @Test
    void test2() {
        // ...
        // b.toString();
        // b.getA().toString();
        // a.toString();
    }

    @AfterMethod
    public void reset()
    {
        // Solution:
        b = null;
    }
}

Mockitos MockitoAnnotations.initMocks(this) only re-initializes mocks, as Mockito.reset(a) only resets mocks. The problem is the class under test, which is annotated with @InjectMocks. This class, here named B, is not initialized again. It is initialized for the first test with a mock of A, the mock of A is re-initialized but B still contains the first version of A.

The solution is to reset the class under test manually with b = null at any plausible place (@AfterMethod or before initMocks). Then Mockito also re-inizialized the class under test B.

like image 23
MichaelCkr Avatar answered Sep 22 '22 22:09

MichaelCkr


You can also use MockitoTestNGListener it is similar to JUnit MockitoJUnitRunner or MockitoExtension.

Example of code:

import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

import java.util.Map;

@Listeners(MockitoTestNGListener.class)
public class MyTest {

    @Mock
    Map map;

    @InjectMocks
    SomeType someType;

    @Test
    void test() {
        // ...
    }
}
like image 32
Slawomir Jaranowski Avatar answered Sep 21 '22 22:09

Slawomir Jaranowski