我最近问了几个jUnit和Mockito问题,我仍然在努力解决这个问题。这些教程都是非常简单的示例,所以我很难扩展我的测试用例以适应我的课程。
我目前正在尝试为我在webapp中的一个代理中的方法编写一些测试用例。该方法与代理内部的其他几种方法交互以验证某些对象。我现在只想测试这一种方法。
这是我试图做的事情:
像我这样创建我的代理的Mockito对象:
MyProcessingAgent mockMyAgent = Mockito.mock(MyProcessingAgent.class);
使用Mockito.when设置存根(希望是正确的术语),如下所示:
Mockito.when(mockMyAgent.otherMethod(Mockito.any(arg1)).thenReturn(requiredReturnArg);
尝试执行我的方法:
List myReturnValue = mockMyAgent.methodThatNeedsTestCase();
我期待myReturnValue
中的内容,但收到0而不是我尝试调试。当我调用该方法时,它永远不会执行。我在方法的第一行有一个调试点,永远不会被触及。
如果我想在一个类的一个方法中执行代码,但强制类中的其他方法(尝试与外部世界中的数据库交互的方法)返回伪造的值。这可能与Mockito一起吗?
看来我目前的方法不是正确的测试方式,但我不确定如何继续前进。我可以模拟我的类,并且正常执行一个方法,而其他方法被存根以返回我的给定值,这样我在测试这个方法时就不必处理数据访问了吗?
答案 0 :(得分:67)
您将Mock
与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
注意:您应该尝试模拟正在测试的类的依赖关系不类本身。
答案 1 :(得分:6)
因此,模拟测试类的想法是测试练习的anathima。你不应该这样做。因为你已经这样做了,你的测试是进入Mockito的嘲弄课程而不是你正在考试的课程。
间谍活动也行不通,因为这只会在间谍类周围提供一个包装/代理。一旦执行在类中,它将不会通过代理,因此不会打到间谍。更新:虽然我相信这对于Spring代理来说是真实的,但对于Mockito间谍来说似乎并非如此。我设置了方法m1()
调用m2()
的方案。我监视对象并将m2()
存根到doNothing
。当我在测试中调用m1()
时,未达到该类的m2()
。 Mockito调用存根。因此,使用间谍来完成所要求的事情是可能的。但是,我要重申,我认为这是不好的做法(恕我直言)。
您应该模拟所测试的类所依赖的所有类。这将允许您控制被测方法调用的方法的行为,因为您可以控制这些方法调用的类。
如果您的类创建了其他类的实例,请考虑使用工厂。
答案 2 :(得分:4)
你差不多了。问题是Class Under Test( CUT )不是为单元测试而构建的 - 它实际上 <{3}}'。
想想这样......
在单元测试中
@Spy
@Mock
所有其他类/服务/数据库(即外部依赖项)为了避免执行您未严格测试的代码,您可以将该代码抽象为可以@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));