为什么模拟课程这么糟糕?

gue*_*rda 45 testing unit-testing mocking

我最近和一位同事讨论了嘲笑问题.他说,嘲弄课程非常糟糕,不应该在少数情况下完成.

他说只有界面应该被嘲笑,否则就是架构错误.

我想知道为什么这句话(我完全信任他)是如此正确?我不知道,并且想要被说服.

我是否想念嘲笑(是的,我读过Martin Fowler的文章)

Jon*_*eet 61

模拟用于协议测试 - 它测试您将如何使用API​​,以及当API相应地做出反应时您将如何反应.

理想情况下(至少在许多情况下),API应该被指定为接口而不是类 - 接口定义协议,类定义至少部分实现.

实际上,模拟框架在模拟类方面往往存在局限性.

根据我的经验,模拟有点过度使用 - 通常你并不真正对确切的交互感兴趣,你真的想要一个存根 ...但是模拟框架可以用来创建存根,你陷入了创建脆弱测试的陷阱嘲笑而不是顽固.尽管如此,这是一个很难的平衡.

  • 每个类都有一个隐式接口,它是它定义的公共方法和构造函数集.将此视为班级的"协议"并没有错.因此,"协议测试"并不意味着不应该嘲笑类.此外,创建模拟并不意味着必须在测试中明确指定每个模拟方法.期望可以是严格的也可以是非严格的,其中非严格的期望在被测试的代码中被自由地允许发生任何次数.换句话说,存根只是一种模拟,默认情况下所有期望都是非严格的. (8认同)
  • @Rogerio:我仍然觉得通过接口指定API更简单,因为这意味着你不会得到"偶然公开"的方法(例如那些用于实现*其他*接口的方法)作为依赖混合的一部分.当你开始只模拟*某些方法,使用其他方法的生产代码时,最终会产生一种令人不舒服的混合物IMO - 因为这意味着你假设你所嘲笑的那些位不受你的位影响.不要反过来......并且不要忘记这个*不是我们正在谈论的被测试的课程. (3认同)

Pas*_*ent 24

恕我直言,你的同事意味着你应该编程到一个接口,而不是一个实现.如果你发现自己经常嘲笑课程,那么这就是你在设计架构时违背了先前原则的标志.

  • +1,这肯定是同事所说的. (3认同)
  • “编程接口...”仅意味着您使用抽象类型而不是具体实现类型声明字段、变量、参数和返回类型。而已。因此,对于没有实现单独抽象类型的类,模拟它(在测试中需要时)将非常好。为这样的类创建一个毫无意义的单独接口只会浪费精力。 (3认同)
  • 我没有具体提到抽象类,只是提到“抽象”和“抽象类型”。Java 接口是一种抽象类型。我很好地理解抽象。不过,我怀疑您可能对“耦合”的真正含义(也许还有“内聚”)感到困惑。在没有明确的业务或技术理由的情况下编写额外的代码是错误的。我已经见识够多了,知道在这样的代码库上工作是多么痛苦。 (2认同)

Ste*_*ger 15

模拟类(与模拟接口相反)是不好的,因为模拟在后台仍然有一个真正的类,它是继承的,并且有可能在测试期间执行真正的实现.

当你模拟(或存根或其他)一个接口时,不存在你实际想要模拟执行代码的风险.

模拟类也迫使你把所有可能被嘲笑的东西变成虚拟的,这是非常侵入性的,可能导致糟糕的课堂设计.

如果你想要解耦类,他们不应该彼此了解,这就是为什么模拟(或存根或其他)其中一个是有意义的.因此,无论如何都建议对接口进行实现,但其他人已经提到了这一点.

  • 通过为受依赖的接口简单地引入一个单独的接口来实现“解耦类”,当该接口永远不会再有第二个实现时,这就是不良设计的真实情况。仅当好处显而易见时,才有理由将抽象与实现分开。否则会过度设计,这可能(确实)给实际的软件项目带来巨大的危害。 (5认同)
  • 您实际上是在谈论某些模拟工具的局限性,而不是在Java语言中模拟引用类型(类,接口,枚举和注释)所固有的内容.模拟一个类时,没有必要创建一个子类.例如,使用JMockit工具,模拟类或接口实际上是相同的(即使类是最终/密封或抽象). (2认同)

And*_*mes 5

通常你会想要模拟一个接口。

虽然可以模拟常规类,但它往往会过多地影响类设计的可测试性。诸如可访问性、方法是否为虚拟等问题都将由模拟类的能力决定,而不是真正的 OO 问题。

有一个叫做 TypeMock Isolator 的伪造库可以让你绕过这些限制(吃蛋糕,吃蛋糕),但它非常昂贵。更好地设计可测试性。


yeg*_*256 5

我建议尽可能远离模拟框架。同时,我建议尽可能使用模拟/假对象进行测试。这里的技巧是您应该将内置的假对象与真实对象一起创建。我在一篇关于它的博客文章中更详细地解释了它:http : //www.yegor256.com/2014/09/23/built-in-fake-objects.html