使用 'this' 关键字调用的 Mockito 存根方法

Mockito stub method invoked using 'this' keyword

我必须测试一些 SLSB 的方法,它调用当前对象的另一个方法(使用 this 关键字),我需要以某种方式存根它。

考虑以下简化代码:

@Local
public interface SomeService{
    public int someMethod();
    public int anotherMethod();
}

@Stateless()
public class SomeServiceImpl{

    @EJB
    private SomeDAO sDAO;

    public SomeServiceImpl(){}

    public SomeServiceImpl(SomeDAO sDAO){
         this.sDAO = sDAO;
    }

    @Override
    public int someMethod(){
        int dbValue = sDAO.getSomeDBValue(); // 1st stub required here
        return dbValue + this.anotherMethod(); // 2nd stub required here
    }

    @Override
    public int anotherMethod(){
         return 5;
    }
}

为了存根 getSomeDBValue() 方法,我可以使用 @Mock 和 @InjectMocks 注释向这个 class 注入模拟,但我不知道如何正确存根 anotherMethod()。为了存根,我肯定需要在模拟对象上做它,所以我尝试将对当前对象的引用作为参数传递,并在测试中只传递模拟对象。 例如,如果我的方法看起来像这样(不需要存根 DAO 方法)..

@Override
public int someMethod(SomeServiceImpl sS){ 
    return sS.anotherMethod(); 
}

我使用手动创建的模拟的测试如下所示:

@Test
public void someMethodTest() throws Exception {
    SomeServiceImpl sS = mock(SomeServiceImpl.class);
    when(sS.someMethod(any(SomeServiceImpl.class))).thenCallRealMethod();
    when(sS.anotherMethod()).thenReturn(5);
    assertEquals(5, sS.someMethod(sS));
}

方法在模拟对象上调用,对对象本身的引用作为参数传递,另一个方法被存根。它有效,但它似乎是非常丑陋的解决方案,如果需要使用这样的注释注入我的 DAO 的模拟怎么办:

@RunWith(MockitoJUnitRunner.class)
public class SomeClassTest{

    @Mock
    SomeDAO sDAO;

    //@Mock //I can't use those 2 annotations at once
    @InjectMocks
    SomeServiceImpl sS; 

    @Test
    public void someMethodTest() throws Exception {
        //...   
    }
}

据我所知,@InjectMocks 注释用于指示 class 应在何处注入带有 @Mock 注释的模拟,但对于我丑陋的解决方案,我也需要模拟 SomeServiceImpl。

我的解决方案是否接近正确?我应该如何存根 anotherMethod() 以正确测试 someMethod()?传递 class 的模拟实例是个好主意,我在方法参数中测试了哪个方法?如果是,我应该如何处理创建带注释的模拟?

您不应该在 same class 上测试另一个方法时模拟 one 方法。理论上你可以这样做(使用 Mokito spy 例如)。

从这个意义上说,您在错误的层面上接近了这个:您实际上不应该关心您的测试方法在您的 class 测试中调用了哪些其他方法。你看,你想测试 someMethod() 确实履行了它的合同。如果这需要在您的生产环境中调用 anotherMethod() ...您的单元测试在模拟 anotherMethod() 时有多大价值?!

另一个想法:您将关注点分开,并将 anotherMethod() 部分移动到它自己的 class X 中。然后您的 class 被测试可以包含 X 的实例;然后可以模拟该实例。