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

时间:2019-07-10 11:23:45

标签: java junit mockito powermock powermockito

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

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

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

因此,我已经模拟了SomeService类来返回我的模拟对象而不是原始的SomeService对象。我已经通过PowerMockito的每种@Test方法做到了这一点

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()每次都会从第一个测试返回数据,尽管事实是,我在每个测试中都使用新数据来模拟它。我尝试在每个@PrepareForTest({aClassToTest.class, SomeService.class})方法的上方添加注释@Test,但是经过几次测试后,我有了一个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”。

3 个答案:

答案 0 :(得分:0)

我不知道为什么这不起作用:

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

答案 1 :(得分:0)

配置初始化时,请使用withNoArguments。同时从SomeService.class

中删除@PrepareForTest
@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本身)的类做准备。

引用How to mock construction of new objects


与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允许我们在其他对象中模拟对象创建的能力,但这并不意味着应该鼓励我们设计难以维护和孤立测试的紧密耦合类。

如果找不到可行的解决方案,此答案也将替代当前方法。

答案 2 :(得分:-1)

谢谢大家。通过更改类的设计解决的问题,应该进行测试。