我知道我如何使用这些术语,但我想知道是否有单独测试的伪造,模拟和存根的可接受定义?你如何为你的测试定义这些?描述您可能使用每种情况的情况。
以下是我如何使用它们:
假:一个实现接口但包含固定数据而没有逻辑的类。根据实施情况,简单地返回“好”或“坏”数据。
Mock:一个实现接口的类,允许动态设置值以返回/异常从特定方法抛出,并提供检查是否已调用/未调用特定方法的功能。
存根:类似于模拟类,但它不提供验证方法是否已被调用/未调用的能力。
模拟和存根可以由模拟框架手动生成或生成。伪造的类是手工生成的。我主要使用模拟来验证我的类和依赖类之间的交互。一旦我验证了交互并且正在通过我的代码测试备用路径,我就会使用存根。我主要使用假类来抽象出数据依赖性,或者每次使用模拟/存根都太繁琐。
如果您熟悉Arrange-Act-Assert,那么解释存根和模拟之间可能对您有用的区别的一种方法是存根属于排列部分,因为它们用于排列输入状态,并且模拟属于断言部分,因为它们用于断言结果。
假人什么都不做。它们只是用于填充参数列表,因此您不会得到未定义或null错误。它们也存在以满足严格类型语言的类型检查器,因此您可以被允许编译和运行。
stub和fake是对象,因为它们可以根据输入参数改变响应。它们之间的主要区别在于假冒更接近真实世界的实现而不是存根。存根包含对预期请求的基本硬编码响应。让我们看一个例子:
public class MyUnitTest {
@Test
public void testConcatenate() {
StubDependency stubDependency = new StubDependency();
int result = stubDependency.toNumber("one", "two");
assertEquals("onetwo", result);
}
}
public class StubDependency() {
public int toNumber(string param) {
if (param == “one”) {
return 1;
}
if (param == “two”) {
return 2;
}
}
}
模拟是假货和存根的一个步骤。模拟提供与存根相同的功能,但更复杂。他们可以为他们定义规则,规定必须调用API上的方法的顺序。大多数模拟可以跟踪调用方法的次数,并可以根据该信息做出反应。模拟通常知道每个调用的上下文,并且可以在不同情况下做出不同的反应。因此,嘲笑需要他们嘲笑的类的一些知识。存根通常无法跟踪调用方法的次数或调用方法序列的顺序。模拟看起来像:
public class MockADependency {
private int ShouldCallTwice;
private boolean ShouldCallAtEnd;
private boolean ShouldCallFirst;
public int StringToInteger(String s) {
if (s == "abc") {
return 1;
}
if (s == "xyz") {
return 2;
}
return 0;
}
public void ShouldCallFirst() {
if ((ShouldCallTwice > 0) || ShouldCallAtEnd)
throw new AssertionException("ShouldCallFirst not first thod called");
ShouldCallFirst = true;
}
public int ShouldCallTwice(string s) {
if (!ShouldCallFirst)
throw new AssertionException("ShouldCallTwice called before ShouldCallFirst");
if (ShouldCallAtEnd)
throw new AssertionException("ShouldCallTwice called after ShouldCallAtEnd");
if (ShouldCallTwice >= 2)
throw new AssertionException("ShouldCallTwice called more than twice");
ShouldCallTwice++;
return StringToInteger(s);
}
public void ShouldCallAtEnd() {
if (!ShouldCallFirst)
throw new AssertionException("ShouldCallAtEnd called before ShouldCallFirst");
if (ShouldCallTwice != 2) throw new AssertionException("ShouldCallTwice not called twice");
ShouldCallAtEnd = true;
}
}