我最近问了几个面向jUnit和Mockito的问题,我仍然在努力掌握它的窍门。这些教程都是针对非常简单的示例,因此我正在努力扩展我的测试用例以适用于我的类。
我目前正试图写一些测试用例的方法,我在我的一个代理在一个web应用程序。该方法与代理中的其他几个方法交互以验证某些对象。我现在只想测试一下这个方法
我是这样做的:
-
创建代理的Mockito对象,如下所示:
MyProcessingAgent mockMyAgent = Mockito.mock(MyProcessingAgent.class);
-
使用Mockito设置存根(希望是正确的术语)。像这样:
Mockito.when(mockMyAgent.otherMethod(Mockito.any(arg1)).thenReturn(requiredReturnArg);
-
试试这样执行我的方法:
List myReturnValue = mockMyAgent.methodThatNeedsTestCase();
我期待的东西在myReturnValue
,但收到0而不是,所以我试图调试。当我调用这个方法时,它永远不会执行。我在方法的第一行有一个调试点,它永远不会被触及。
如果我想在类的一个方法中执行代码,但强制类中的其他方法(试图与外部数据库交互的方法)返回伪造的值。这对于Mockito来说是否可行?
看来我目前的方法不是一种正确的测试风格,但我不确定如何前进。我可以模拟我的类,让一个方法像正常一样执行,而其他方法存根返回我给定的值,这样我就不必在测试这个方法期间处理数据访问了吗?
您混淆了Mock
和Spy
。
在模拟中,所有方法都是存根的,并返回"智能返回类型"。这意味着在模拟类上调用任何方法都不会做任何事情,除非你指定了行为。
在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));