使用Mockito存根并执行测试方法

时间:2013-04-12 16:04:16

标签: java junit mocking mockito

我最近问了几个jUnit和Mockito问题,我仍然在努力解决这个问题。这些教程都是非常简单的示例,所以我很难扩展我的测试用例以适应我的课程。

我目前正在尝试为我在webapp中的一个代理中的方法编写一些测试用例。该方法与代理内部的其他几种方法交互以验证某些对象。我现在只想测试这一种方法。

这是我试图做的事情:

  1. 像我这样创建我的代理的Mockito对象:

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

  2. 使用Mockito.when设置存根(希望是正确的术语),如下所示:

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

  3. 尝试执行我的方法:

    List myReturnValue = mockMyAgent.methodThatNeedsTestCase();

  4. 我期待myReturnValue中的内容,但收到0而不是我尝试调试。当我调用该方法时,它永远不会执行。我在方法的第一行有一个调试点,永远不会被触及。

    如果我想在一个类的一个方法中执行代码,但强制类中的其他方法(尝试与外部世界中的数据库交互的方法)返回伪造的值。这可能与Mockito一起吗?

    看来我目前的方法不是正确的测试方式,但我不确定如何继续前进。我可以模拟我的类,并且正常执行一个方法,而其他方法被存根以返回我的给定值,这样我在测试这个方法时就不必处理数据访问了吗?

4 个答案:

答案 0 :(得分:67)

您将MockSpy混淆。

在模拟中,所有方法都被存根并返回“智能返回类型”。这意味着除非您指定行为,否则调用模拟类上的任何方法什么都不做

在间谍中,类的原始功能仍然存在,但您可以在间谍中验证方法调用,并覆盖方法行为。

你想要的是

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

注意:您应该尝试模拟正在测试的类的依赖关系类本身。

答案 1 :(得分:6)

因此,模拟测试类的想法是测试练习的anathima。你不应该这样做。因为你已经这样做了,你的测试是进入Mockito的嘲弄课程而不是你正在考试的课程。

间谍活动也行不通,因为这只会在间谍类周围提供一个包装/代理。一旦执行在类中,它将不会通过代理,因此不会打到间谍。更新:虽然我相信这对于Spring代理来说是真实的,但对于Mockito间谍来说似乎并非如此。我设置了方法m1()调用m2()的方案。我监视对象并将m2()存根到doNothing。当我在测试中调用m1()时,未达到该类的m2()。 Mockito调用存根。因此,使用间谍来完成所要求的事情是可能的。但是,我要重申,我认为这是不好的做法(恕我直言)。

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

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

答案 2 :(得分:4)

你差不多了。问题是Class Under Test CUT )不是为单元测试而构建的 - 它实际上 <{3}}'。

想想这样......

  • 我需要测试一个类的功能 - 让我们称之为 myFunction
  • 该函数调用另一个类/ service / database上的函数
  • 该功能还会在 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并验证使用 foo 变量调用.getMeAnObject()。

答案 3 :(得分:0)

简短回答

如何做你的情况:

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

LONG ANSWER

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

让我们模拟对DataBase查询的调用。此查询返回具有FreeRoms = X和StarNumber = Y的HotelTable的所有行。 我期望在测试期间,这个查询将返回不同酒店的列表:每个返回的酒店具有相同的值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));