提问者:小点点

将Mockito与TestNG一起使用


我使用Mockito为JUnit编写了一个工作测试,并试图使其适应TestNG,但奇怪的是使用TestNG只有一个测试可以工作。

我认为它在某种程度上与模拟的重置有关,但我已经尝试调用Mockito。重置并使用BeforeMethod和BeforeClass以及不同的组合,但仍然只能通过一个测试。

我需要做什么才能让测试生效?

@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());
}

第二个测试似乎总是在断言内容类型未设置或预期结果错误时失败。

似乎第一次测试的反应在第二次测试中被评估了,所以显然是错误的!

我知道控制器和服务按预期工作,使用jUnit运行的完全相同的测试实际上工作正常。

只有当我执行以下操作时,我才能正确执行测试:

 @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());
}

这意味着我需要为每个测试方法添加一个这些重置方法,然后我需要一个似乎不正确的每个测试方法组。


共3个答案

匿名用户

这些框架的行为有所不同:

  • JUnit为其每个测试方法创建一个新的类实例。这意味着测试之间不共享字段。
  • 但是TestNG只创建一个对象,因此字段中的状态在@Tests
  • 之间共享

对于Mockito,您需要在每个测试方法之前初始化模拟,以便状态不会在TestNG中的两个@Test之间共享:

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

对于JUnit,它可以开箱即用,因为第二个@Test有自己的字段和自己的模拟。

匿名用户

我不确定这个答案是否适合提问者的问题,因为模拟没有列出。但是我想重申安迪在接受的答案中的一个评论,它帮助我解决了同样的问题。这里有一些更多的细节和一个例子:

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;
    }
}

MockitosMockitoAnnotations. initMocks(this)只会重新初始化mock,因为Mockito.set(a)只会重置mock。问题是被测试的类,它用@InjectMocks进行了注释。这个类,这里命名为B,不会再次初始化。它在第一次测试中使用A的mock进行初始化,A的mock被重新初始化,但B仍然包含A的第一个版本。

解决方案是在任何可能的位置(@AftermethodinitMocks之前)手动重置被测类,使用b=null。然后Mockito还重新iniziation了被测类B

匿名用户

您还可以使用MockitoTestNGListener,它类似于JUnitMockitoJUnitRunnerMockitoExency

代码示例:

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() {
        // ...
    }
}