我知道如何使用这些术语,但我想知道单元测试是否有伪造、模拟和存根的公认定义?如何为您的测试定义这些?描述一下你可能会用到它们的场景。
以下是我如何使用它们:
Fake:实现接口但包含固定数据且没有逻辑的类。只是根据实现返回“好”或“坏”数据。
Mock:实现接口的类,允许从特定方法动态设置返回值/抛出异常,并提供检查特定方法是否被调用/未调用的能力。
Stub:类似于模拟类,只是它不提供验证方法是否被调用的能力。
模拟和存根可以手工生成,也可以由模拟框架生成。假类是手工生成的。我使用模拟主要是为了验证我的类和依赖类之间的交互。一旦我验证了交互并在代码中测试替代路径,我就使用存根。我使用伪类主要是为了抽象出数据依赖关系,或者当mock /存根太乏味而每次都无法设置时。
为了说明存根和模拟的用法,我还想包括一个基于Roy Osherove的“单元测试的艺术”的例子。
想象一下,我们有一个LogAnalyzer应用程序,它的唯一功能是打印日志。它不仅需要与web服务对话,而且如果web服务抛出错误,LogAnalyzer必须将错误记录到不同的外部依赖项,通过电子邮件发送给web服务管理员。
下面是我们想要在LogAnalyzer中测试的逻辑:
if(fileName.Length<8)
{
try
{
service.LogError("Filename too short:" + fileName);
}
catch (Exception e)
{
email.SendEmail("a","subject",e.Message);
}
}
当web服务抛出异常时,如何测试LogAnalyzer正确地调用电子邮件服务?
以下是我们面临的问题:
我们如何替换web服务?
我们如何模拟来自web服务的异常,以便我们可以
测试对电子邮件服务的调用?
我们如何知道电子邮件服务是否被正确调用
所有的吗?
我们可以通过使用web服务的存根来处理前两个问题。为了解决第三个问题,我们可以为电子邮件服务使用一个模拟对象。
A fake is a generic term that can be used to describe either a stub or a mock.In our test, we’ll have two fakes. One will be the email service mock, which we’ll use to verify that the correct parameters were sent to the email service. The other will be a stub that we’ll use to simulate an exception thrown from the web service. It’s a stub because we won’t be using the web service fake to verify the test result, only to make sure the test runs correctly. The email service is a mock because we’ll assert against it that it was called correctly.
[TestFixture]
public class LogAnalyzer2Tests
{
[Test]
public void Analyze_WebServiceThrows_SendsEmail()
{
StubService stubService = new StubService();
stubService.ToThrow= new Exception("fake exception");
MockEmailService mockEmail = new MockEmailService();
LogAnalyzer2 log = new LogAnalyzer2();
log.Service = stubService
log.Email=mockEmail;
string tooShortFileName="abc.ext";
log.Analyze(tooShortFileName);
Assert.AreEqual("a",mockEmail.To); //MOCKING USED
Assert.AreEqual("fake exception",mockEmail.Body); //MOCKING USED
Assert.AreEqual("subject",mockEmail.Subject);
}
}
为了说明存根和模拟的用法,我还想包括一个基于Roy Osherove的“单元测试的艺术”的例子。
想象一下,我们有一个LogAnalyzer应用程序,它的唯一功能是打印日志。它不仅需要与web服务对话,而且如果web服务抛出错误,LogAnalyzer必须将错误记录到不同的外部依赖项,通过电子邮件发送给web服务管理员。
下面是我们想要在LogAnalyzer中测试的逻辑:
if(fileName.Length<8)
{
try
{
service.LogError("Filename too short:" + fileName);
}
catch (Exception e)
{
email.SendEmail("a","subject",e.Message);
}
}
当web服务抛出异常时,如何测试LogAnalyzer正确地调用电子邮件服务?
以下是我们面临的问题:
我们如何替换web服务?
我们如何模拟来自web服务的异常,以便我们可以
测试对电子邮件服务的调用?
我们如何知道电子邮件服务是否被正确调用
所有的吗?
我们可以通过使用web服务的存根来处理前两个问题。为了解决第三个问题,我们可以为电子邮件服务使用一个模拟对象。
A fake is a generic term that can be used to describe either a stub or a mock.In our test, we’ll have two fakes. One will be the email service mock, which we’ll use to verify that the correct parameters were sent to the email service. The other will be a stub that we’ll use to simulate an exception thrown from the web service. It’s a stub because we won’t be using the web service fake to verify the test result, only to make sure the test runs correctly. The email service is a mock because we’ll assert against it that it was called correctly.
[TestFixture]
public class LogAnalyzer2Tests
{
[Test]
public void Analyze_WebServiceThrows_SendsEmail()
{
StubService stubService = new StubService();
stubService.ToThrow= new Exception("fake exception");
MockEmailService mockEmail = new MockEmailService();
LogAnalyzer2 log = new LogAnalyzer2();
log.Service = stubService
log.Email=mockEmail;
string tooShortFileName="abc.ext";
log.Analyze(tooShortFileName);
Assert.AreEqual("a",mockEmail.To); //MOCKING USED
Assert.AreEqual("fake exception",mockEmail.Body); //MOCKING USED
Assert.AreEqual("subject",mockEmail.Subject);
}
}
单元测试——是一种控制单元(类、方法)的测试方法。
Test double -不是一个主对象(来自OOP世界)。这是一种临时创建的实现,用于测试、检查或在开发期间。它们是为关闭被测试单元(方法、类……)的依赖关系而创建的。
测试双工类型:
伪对象是接口(协议)的真实实现或扩展,它使用继承或其他方法来创建- is依赖。通常,它是由开发人员创建的,作为替代某些依赖项的最简单解决方案
存根对象是一个裸对象(0,nil和没有逻辑的方法),具有(由开发人员)预定义的额外状态,以定义返回值。通常是由框架创建的
class StubA: A {
override func foo() -> String {
return "My Stub"
}
}
模拟对象与存根对象非常相似,但是额外的状态在程序执行期间被改变,以检查是否发生了什么(方法被调用,参数,时间,频率……)
class MockA: A {
var isFooCalled = false
override func foo() -> String {
isFooCalled = true
return "My Mock"
}
}
间谍对象是一个具有“部分嘲讽性”的真实对象。这意味着除了模拟行为外,您使用的是一个非双精度对象
虚拟对象是运行测试所必需的对象,但该对象的任何变量或方法都没有被调用。
Stub vs mock
马丁·福勒说
存根使用状态验证,而mock使用行为验证,这是不同的。
[Mockito mock vs spy]