我知道如何使用这些术语,但我想知道单元测试是否有伪造、模拟和存根的公认定义?如何为您的测试定义这些?描述一下你可能会用到它们的场景。

以下是我如何使用它们:

Fake:实现接口但包含固定数据且没有逻辑的类。只是根据实现返回“好”或“坏”数据。

Mock:实现接口的类,允许从特定方法动态设置返回值/抛出异常,并提供检查特定方法是否被调用/未调用的能力。

Stub:类似于模拟类,只是它不提供验证方法是否被调用的能力。

模拟和存根可以手工生成,也可以由模拟框架生成。假类是手工生成的。我使用模拟主要是为了验证我的类和依赖类之间的交互。一旦我验证了交互并在代码中测试替代路径,我就使用存根。我使用伪类主要是为了抽象出数据依赖关系,或者当mock /存根太乏味而每次都无法设置时。


当前回答

如果您熟悉arrange - act - assert,那么解释存根和mock之间的区别的一种方法可能对您有用,即存根属于arrange部分,因为它们用于排列输入状态,而mock属于assert部分,因为它们用于断言结果。

傻瓜什么都不做。它们只是用来填充参数列表,这样你就不会得到未定义或空错误。它们的存在也是为了满足静态类型语言中的类型检查器,这样您就可以编译和运行。

其他回答

Stub, Fakes和Mocks在不同的来源中有不同的含义。我建议你介绍一下你的团队内部术语,并就其含义达成一致。

我认为区分两种方法很重要: -行为验证(暗示行为替换) -最终状态验证(暗示行为模拟)

考虑发送电子邮件以防出错。当做行为验证时,你检查IEmailSender的Send方法是否执行了一次。并且您需要模拟此方法的返回结果,返回发送消息的Id。所以你说:“我期望Send会被调用。我只会为任何调用返回虚拟(或随机)Id”。这就是行为验证: emailSender.Expect(es=> . send (anyThing)).Return((subject,body) => "dummyId")

在进行状态验证时,您需要创建实现IEmailSender的TestEmailSender。并实现发送方法-通过将输入保存到一些数据结构,将用于未来的状态验证,如一些对象的数组,然后它测试你将检查sentemail包含预期的电子邮件。这是状态验证: 断言。emailSender.SentEmails.Count AreEqual (1)

从我的阅读中,我了解到行为验证通常被称为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);
 }
}

我倾向于使用两个术语-假和Mock。

只有在使用像Moq这样的Mock框架时才使用Mock,因为当它使用new Mock<ISomething>()创建时,将其称为Fake似乎是不正确的-虽然从技术上讲,您可以使用Mock框架创建stub或Fakes,但在这种情况下这样称呼它似乎有点愚蠢-它必须是Mock。

其他都是假的。如果一个Fake可以被总结为一个功能降低的实现,那么我认为一个Stub也可以是一个Fake(如果不是,谁在乎呢,每个人都知道我的意思,而且从来没有人说过“我认为你会发现那是一个Stub”)

你在它上面断言的东西叫做模拟对象。

其他帮助测试运行的东西都是存根。

在Gerard Meszaros所著的《xUnit Test Patterns》一书中,有一个很好的表格给出了关于差异的很好的见解