mockito 如何在某个服务中模拟创建新对象?

7jmck4yq  于 2022-11-08  发布在  其他
关注(0)|答案(3)|浏览(174)

我必须在某个类中测试一个方法,如下所示:

public class aClassToTest{
  private SomeService someService = new SomeService();

  public String methodToTest(){
  String data = someService.getData();
  //....
 }
}

因此,我已经模拟了SomeService类返回我的mock-object而不是原始的SomeService-object。

SomeService someServiceMock = mock(SomeService.class); 
when(someServiceMock.getData().thenReturn(Data myMockedData)
PowerMockito.whenNew(SomeService.class).withAnyArguments().thenReturn(someServiceMock);

在我的Test类上面有这样的注解:

@PrepareForTest({aClassToTest.class, SomeService.class})

如果只有一个测试,它可以正常工作,但是如果有几个测试,someServiceMock.getData()每次都会返回第一个测试的数据,尽管事实上,我在每个测试中都用新数据模拟它。我曾试图在每个@Test方法上添加注解@PrepareForTest({aClassToTest.class, SomeService.class}),但是现在在几个测试后,我得到了一个OutOfMemoryError。现在它只有在我用所有测试方法运行整个Test类时才能工作,但是如果我单独运行测试方法,我会有No tests found for given includes错误。
我有这样的测试课:

@RunWith(PowerMock.class)
@PrepareForTest({aClassToTest.class, SomeService.class})
public class TestClass{

private void doMockSomeService(String testData){
  SomeService someServiceMock = mock(SomeService.class); 
  when(someServiceMock.getData().thenReturn(testData);
  PowerMockito.whenNew(SomeService.class).withAnyArguments().thenReturn(someServiceMock);
 }

  @Test
  public void testCase1(){
  String expectedResult = "expectedResult1";
  doMockSomeService("123");
  ClassToTest classToTest = new ClassToTest();
  String result = classToTest.methodToTest();
  assertEquals(result, expectedResult);
  }

 @Test
  public void testCase2(){
  String expectedResult = "expectedResult2";
  doMockSomeService("456");
  ClassToTest classToTest = new ClassToTest();
  String result = classToTest.methodToTest();
  assertEquals(result, expectedResult);
  }
}

在本例中,someService.getData()的返回值始终为“123”。

qkf9rpyu

qkf9rpyu1#

我不知道为什么这样不行:

@RunWith(MockitoJUnitRunner.class)
class MyTest {
    @Mock private SomeService service;
    @InjectMocks private aClassToTest;

    @Before
    public void initData() {
        when(service.getData()).thenReturn(data);
    }

    @Test
    public void test() {
        mockData("expected");

        String result = aClassToTest.methodToTest();
        verify(service).getData(); // method was called

        assertEquals("expected", result);
    }

    private void mockData(String str) {
        when(service.getData()).thenReturn(str);
    }
}
a64a0gku

a64a0gku2#

在配置初始化时使用withNoArguments。同时从@PrepareForTest中删除SomeService.class

@RunWith(PowerMockRunner.class)
@PrepareForTest(aClassToTest.class)
public class TestClass {

    private void doMockSomeService(String testData){
        SomeService someServiceMock = PowerMockito.mock(SomeService.class); 
        PowerMockito.when(someServiceMock.getData().thenReturn(testData);
        PowerMockito.whenNew(SomeService.class).withNoArguments()
            .thenReturn(someServiceMock);
    }

    @Test
    public void testCase1() {
        //Arrange
        String expectedResult = "expectedResult1";
        doMockSomeService("123");
        ClassToTest classToTest = new ClassToTest();
        //Act
        String result = classToTest.methodToTest();
        //Assert
        assertEquals(result, expectedResult);
    }

    @Test
    public void testCase2() {
        //Arrange
        String expectedResult = "expectedResult2";
        doMockSomeService("456");
        ClassToTest classToTest = new ClassToTest();
        //Act
        String result = classToTest.methodToTest();
        //Assert
        assertEquals(result, expectedResult);
    }
}

请注意,您必须准备创建SomeService新示例的类以进行测试,而不是SomeService本身。
如何模拟新对象的构造
与powermock遇到的问题不同,这演示了将代码与依赖项紧密耦合的问题,这些依赖项使隔离测试变得困难。
一种更可靠的方法是使用依赖关系反转来遵循显式依赖关系原则

public class aClassToTest{
    private SomeService someService;

    @Inject    
    public aClassToTest(SomeService someService) {
        this.someService = someService;
    }

    public String methodToTest(){
        String data = someService.getData();
        //....
    }
}

这会将依赖关系的创建转换为目标类的外部依赖关系,并且非常清楚地说明该类执行其特定功能所需的内容。
这也允许在测试中对目标主题进行更简单的独立单元测试

@RunWith(MockitoJUnitRunner.class)
public class TestClass {

    private someServiceMock doMockSomeService(String testData){
        someServiceMock = mock(SomeService.class); 
        when(someServiceMock.getData().thenReturn(testData);
        return someServiceMock;
    }

    @Test
    public void testCase1() {
        //Arrange
        String expected = "expectedResult1";
        ClassToTest classToTest = new ClassToTest(doMockSomeService("123"));
        //Act
        String actual = classToTest.methodToTest();
        //Assert
        assertEquals(expected, actual);
    }

    @Test
    public void testCase2() {
        //Arrange
        String expected = "expectedResult2";
        ClassToTest classToTest = new ClassToTest(doMockSomeService("456"));
        //Act
        String actual = classToTest.methodToTest();
        //Assert
        assertEquals(expected, actual);
    }
}

在我看来,仅仅因为PowerMock允许我们模拟在其他对象中创建对象的能力,并不意味着应该鼓励我们设计难以单独维护和测试的紧密耦合的类。
如果找不到可行的解决方案,这个答案也是目前方法的替代方案。

8wigbo56

8wigbo563#

谢谢大家。通过改变类的设计解决了问题,应该测试一下。

相关问题