使用Mockito来存根和执行测试方法



我最近问了几个面向jUnit和Mockito的问题,我仍然在努力掌握它的窍门。这些教程都是针对非常简单的示例,因此我正在努力扩展我的测试用例以适用于我的类。

我目前正试图写一些测试用例的方法,我在我的一个代理在一个web应用程序。该方法与代理中的其他几个方法交互以验证某些对象。我现在只想测试一下这个方法

我是这样做的:

  1. 创建代理的Mockito对象,如下所示:

    MyProcessingAgent mockMyAgent = Mockito.mock(MyProcessingAgent.class);

  2. 使用Mockito设置存根(希望是正确的术语)。像这样:

    Mockito.when(mockMyAgent.otherMethod(Mockito.any(arg1)).thenReturn(requiredReturnArg);

  3. 试试这样执行我的方法:

    List myReturnValue = mockMyAgent.methodThatNeedsTestCase();

我期待的东西在myReturnValue,但收到0而不是,所以我试图调试。当我调用这个方法时,它永远不会执行。我在方法的第一行有一个调试点,它永远不会被触及。

如果我想在类的一个方法中执行代码,但强制类中的其他方法(试图与外部数据库交互的方法)返回伪造的值。这对于Mockito来说是否可行?

看来我目前的方法不是一种正确的测试风格,但我不确定如何前进。我可以模拟我的类,让一个方法像正常一样执行,而其他方法存根返回我给定的值,这样我就不必在测试这个方法期间处理数据访问了吗?

您混淆了MockSpy

在模拟中,所有方法都是存根的,并返回"智能返回类型"。这意味着在模拟类上调用任何方法都不会做任何事情,除非你指定了行为。

在spy中,类的原始功能仍然存在,但是你可以在spy中验证方法调用,也可以覆盖方法行为。

你想要的是

MyProcessingAgent mockMyAgent = Mockito.spy(MyProcessingAgent.class);

一个简单的例子:

static class TestClass {
    public String getThing() {
        return "Thing";
    }
    public String getOtherThing() {
        return getThing();
    }
}
public static void main(String[] args) {
    final TestClass testClass = Mockito.spy(new TestClass());
    Mockito.when(testClass.getThing()).thenReturn("Some Other thing");
    System.out.println(testClass.getOtherThing());
}

输出是:

Some Other thing
注意:你应该试着模拟被测试类的依赖关系而不是类本身。

所以,模拟被测试类的想法是对测试实践的厌恶。你不应该这样做。因为您已经这样做了,所以您的测试正在进入Mockito的mock类,而不是您的测试类。

间谍也不能工作,因为这只提供了一个包装器/代理围绕间谍类。一旦在类内部执行,它就不会经过代理,因此也就不会碰到间谍。更新:虽然我相信这对Spring代理是正确的,但对Mockito间谍似乎不是这样。我设置了一个场景,其中方法m1()调用m2()。我监视对象和存根m2()doNothing。当我在测试中调用m1()时,没有到达类的m2()。Mockito调用存根。所以用间谍来完成任务是可能的。然而,我要重申,我认为这是不好的做法(IMHO)。

应该模拟被测试类所依赖的所有类。这将允许您控制被测方法调用的方法的行为,因为您可以控制这些方法调用的类。

如果你的类创建了其他类的实例,考虑使用工厂。

你就快成功了。问题是被测类(CUT)不是为单元测试而构建的——它并没有真正的被TDD化。

这样想& help;

    我需要测试一个类的函数——让我们把它命名为myFunction
  • 该函数调用另一个类/服务/数据库上的函数
  • 该函数还调用CUT
  • 的另一个方法。

在单元测试中

  • 应该创建一个具体的CUT@Spy
  • 你可以@Mock所有其他类/服务/数据库(即外部依赖)
  • 可以存根CUT中调用的其他函数,但这并不是真正的单元测试应该如何完成

为了避免执行你没有严格测试的代码,你可以将这些代码抽象成可以被@Mock编辑的东西。

在这个非常简单的例子中,创建对象的函数将很难测试

public void doSomethingCool(String foo) {
    MyObject obj = new MyObject(foo);
    // can't do much with obj in a unit test unless it is returned
}

但是使用服务获取MyObject的函数很容易测试,因为我们已经将难以/不可能测试的代码抽象为使该方法可测试的东西。

public void doSomethingCool(String foo) {
    MyObject obj = MyObjectService.getMeAnObject(foo);
}

作为MyObjectService可以被模拟,也可以验证. getmeanobject()是用foo变量调用的。

简短回答

如何处理你的情况:

int argument = 5; // example with int but could be another type
Mockito.when(mockMyAgent.otherMethod(Mockito.anyInt()).thenReturn(requiredReturnArg(argument));
长回答

实际上你想做的是可能的,至少在Java 8中。也许你没有从其他人那里得到这个答案,因为我使用的是Java 8,它允许这样做,这个问题是在Java 8发布之前(它允许传递函数,而不仅仅是值给其他函数)。

让我们模拟对数据库查询的调用。这个查询返回HotelTable中FreeRoms = X和StarNumber = Y的所有行。我在测试期间期望的是,这个查询将返回一个不同酒店的列表:每个返回的酒店具有相同的值X和Y,而其他值和我将根据我的需要决定它们。下面的例子很简单,但当然你可以把它变得更复杂。

我创建了一个函数返回不同的结果但它们都是FreeRoms = X和StarNumber = y

static List<Hotel> simulateQueryOnHotels(int availableRoomNumber, int starNumber) {
    ArrayList<Hotel> HotelArrayList = new ArrayList<>();
    HotelArrayList.add(new Hotel(availableRoomNumber, starNumber, Rome, 1, 1));
    HotelArrayList.add(new Hotel(availableRoomNumber, starNumber, Krakow, 7, 15));
    HotelArrayList.add(new Hotel(availableRoomNumber, starNumber, Madrid, 1, 1));
    HotelArrayList.add(new Hotel(availableRoomNumber, starNumber, Athens, 4, 1));
    return HotelArrayList;
}

也许Spy更好(请尝试),但我在一个模拟类上做了这个。下面是我的做法(注意anyInt()的值):

//somewhere at the beginning of your file with tests...
@Mock
private DatabaseManager mockedDatabaseManager;
//in the same file, somewhere in a test...
int availableRoomNumber = 3;
int starNumber = 4;
// in this way, the mocked queryOnHotels will return a different result according to the passed parameters
when(mockedDatabaseManager.queryOnHotels(anyInt(), anyInt())).thenReturn(simulateQueryOnHotels(availableRoomNumber, starNumber));

相关内容

  • 没有找到相关文章

最新更新