模拟,存根和假对象有什么区别 [英] What is the difference between mocks, stubs and fake objects

查看:114
本文介绍了模拟,存根和假对象有什么区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

尽管资源很多,即使在SO上也是如此,但在这些Q/A中只有两个术语被相互比较.

Although there are plenty of resources, even here on SO, only two of the terms are compared to each other in these Q/A.

因此,简而言之,它们各自是什么?以及它们之间如何相互联系?还是根本不?

So, in short, what is each one of them? And how they all relate to each other? Or don't they at all?

推荐答案

模拟与存根之间的区别非常简单-模拟可以使测试失败,而存根不能.这就是全部.此外,您可以将存根视为提供值的内容.如今,假冒对他们俩来说只是一个通用名词(稍后会详细介绍).

Difference between mock and stub is very simple - mock can make your test fail, while stub can't. That's all there is. Additionally, you can think of stub as of something that provides values. Nowadays, fake is just a generic term for both of them (more on that later).

让我们考虑一种情况,您必须构建一个通过通信协议发送程序包的服务(确切的细节无关紧要).您只需提供带有包装代码的服务,其余的工作即可完成.给定以下代码段,您可以确定潜在的单元测试中哪个依赖项是存根,哪个模拟吗?

Let's consider a case where you have to build a service that sends packages via communication protocol (exact details are irrelevant). You simply supply service with package code and it does the rest. Given the snippet below, can you identify which dependency would be a stub and which mock in potential unit test?

public class DistributionService
{
    public double SendPackage(string packageCode)
    {
        var contents = this.packageService.GetPackageContents(packageCode);
        if (contents == null)
        {
            throw new InvalidOperationException(
                "Attempt to send non-exisiting package");
        }

        var package = this.packageBuilder.Build(contents);
        this.packageDistributor.Send(package);
    }
}

很容易说出packageBuilder只是提供了价值,并且没有任何可能使任何测试失败的方法.这是一个存根.即使看起来似乎更模糊,packageService也是存根.它提供了一个值(从存根的角度来看,我们与该值无关).当然,稍后我们将使用该值来测试是否引发了异常,但是它仍然在我们的控制范围内(例如,我们确切地告诉存根该做什么,而忽略了它–它对测试没有任何进一步的影响).

It's fairly easy to tell that packageBuilder simply provides value and there's no possible way it could make any test fail. That's a stub. Even though it might seem more blurry, packageService is stub too. It provides a value (what we do with the value is irrelevant from stub's point of view). Of course, later we'll use that value to test whether exception is thrown, but it's still all within our control (as in, we tell stub exactly what to do and forget about it - it should have no further influence on test).

packageDistributor有所不同.即使提供任何值,也不会消耗它.然而,对Send的调用似乎是我们实施中非常重要的部分,我们很可能希望对其进行 verify 验证.

It gets different with packageDistributor. Even if it provides any value, it's not consumed. Yet the call to Send seems to be pretty important part of our implementation and we'll most likely want to verify it is called.

在这一点上,我们应该得出一个结论,即packageDistributor模拟.我们将进行专门的单元测试,断言是否调用了Send方法,如果由于某些原因而没有调用-我们想知道,因为它是整个过程的重要组成部分.其他依赖项是存根,因为它们所做的只是为其他可能更相关的代码段提供值.

At this point we should get to a conclusion that packageDistributor is a mock. We'll have a dedicated unit test asserting that Send method was called and if for some reasons it wasn't - we want to know that, as it's important part of the entire process. Other dependencies are stubs as all they do is provide values to other, perhaps more relevant pieces of code.

存根为存根,也可以在未实现的实施中用常量值替换:

Stub being stub, could be just as well replaced with constant value in naive implementation:

var contents = "Important package";
var package = "<package>Important package</package>";
this.packageDistributor.Send(package);

本质上,这是模拟框架对存根的作用-指示它们返回可配置/显式的值. 老式的手工存根通常就是这样做的-返回常数值.

This is essentially what mocking frameworks do with stubs - instruct them to return configurable/explicit value. Old-school, hand-rolled stubs often do just that - return constant value.

显然,这样的代码没有多大意义,但是曾经做过TDD的任何人肯定在类开发的早期就看到了很多这样的天真的实现. TDD产生的迭代开发通常可以帮助识别类的依赖.

Obviously, such code doesn't make much sense, but anyone who ever done TDD surely seen bunch of such naive implementations at the early stage of class development. Iterative development that results from TDD will often help identify roles of your class' dependencies.

在本文开头,我提到 fake 只是一个通用术语.鉴于模拟也可以用作存根(特别是在涉及现代模拟框架时),为避免混淆,将此类对象称为伪造是个好主意.如今,您可以看到这种趋势正在增长-原始的 mock-stub 区别正逐渐成为过去,使用了更多通用名称.例如:

At the beginning of this post I mentioned that fake is just a generic term. Given that mock can also serve as stub (especially when modern mocking frameworks are concerned), to avoid confusion it's good idea to call such object a fake. Nowadays, you can see this trend growing - original mock - stub distinction is slowly becoming a thing of the past and more universal names are used. For example:

  • FakeItEasy使用 fake
  • NSubstitute使用替代
  • Moq使用 mock (名称很旧,但是无论是存根还是模拟都没有明显的区别)
  • FakeItEasy uses fake
  • NSubstitute uses substitute
  • Moq uses mock (name is old, but no visible distinction is made whether it's stub or mock)
    马丁·福勒(Martin Fowler)的
  • Mo子不是桩 -您可以看到这篇文章实际上在任何存根/模拟问题下都被链接了,这是有原因的
  • 探索测试双打的连续性 Mark Seemann撰写-所有令人困惑的单元测试术语的概述(如果您认为模拟,假冒和存根就足够了,您可能应该知道还有* dummy **, spy double ,不是)
  • xunitpatterns.com -庞大的单元测试模式书的支持网站, xUnit测试模式:重构测试代码,作者Gerard Meszaros
  • 单元测试的艺术 Roy Osherove撰写的-出色的单元测试入门书(模拟会使测试失败,但不要存根" 是Roy的话,不是我的)
  • Mocks aren't stubs by Martin Fowler - you can see this article being linked virtually under any stub/mock question, and it's for a reason
  • Exploring The Continuum Of Test Doubles by Mark Seemann - overview for all the confusing unit testing terminology (if you thought mock, fake and stub are enough, you probably should know there's also *dummy**, spy, double and what not)
  • xunitpatterns.com - backing-website for huge unit-testing-patterns book, xUnit Test Patterns: Refactoring Test Code by Gerard Meszaros
  • Art of Unit Testing by Roy Osherove - excellent unit testing introductory book (the "mock can make test fail, stub not" are Roy's words, not mine)

这篇关于模拟,存根和假对象有什么区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆