接口和单元测试——总是白盒测试? [英] Interfaces and unit tests - always white-box testing?

查看:26
本文介绍了接口和单元测试——总是白盒测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我终于明白了是什么让我担心依赖注入和应该使单元测试更容易的类似技术.我们以这个例子为例:

I have finally got in my mind what worried me about Dependency Injection and similar techniques that should make unit tests easier. Let's take this example:

public interface IRepository { void Item Find(); a lot of other methods here; }
[Test]
public void Test()
{
  var repository = Mock<IRepository>();
  repository.Expect(x => x.Find());
  var service = new Service(repository);
  service.ProcessWithItem();
}

现在,上面的代码有什么问题?这是我们的测试粗略地窥视 ProcessWithItem() 实现.如果它想做from x in GetAll() where x..." - 但是不,我们的测试知道那里会发生什么.这只是一个简单的例子.想象一下我们的测试现在与之相关的几个调用,当我们想在方法内从 GetAll() 更改为更好的 GetAllFastWithoutStuff() 时……我们的测试被破坏了.请更改它们.很多蹩脚的工作经常在没有任何实际需要的情况下发生.

Now, what's wrong with the code above? It's that our test roughly peeks into ProcessWithItem() implementation. What if it wants to do "from x in GetAll() where x..." - but no, our test knows what is going to happen there. And that's just a simple example. Imaging few calls that our test now is tied with, and when we want to change from GetAll() to a better GetAllFastWithoutStuff() inside the method... our test(s) are broken. Please change them. A lot of crappy work that happens so often without any real need.

这就是经常让我停止编写测试的原因.我只是不知道如何在不知道实现细节的情况下进行测试.了解它们后,测试现在变得非常脆弱和痛苦.

And that's what often makes me to stop write tests. I just don't see how I can test without knowing implementation details. And knowing them, tests are now very fragile and pain to do.

当然,这不仅仅是关于接口(或 DI).POCO(和 POJO,为什么不)也遭受同样的事情,但它们现在与数据相关联,而不是与接口相关联.但原理是一样的——我们的最终断言与我们对 SUT 将要做什么的了解紧密结合.是的,您必须提供此字段,先生,最好具有此值".

Sure, it's not about interface (or DI) only. POCOs (and POJOs, why not) also suffer from the same thing, but they're now tied with the data, not with the interface. But the principle is the same - our final assertion is tightly coupled with our knowledge of what our SUT is going to do. "Yes you HAVE to provide this field, sir, and this better be of this value".

因此,测试将会失败 - 很快而且经常发生.这是痛苦.还有问题.

As a consequence, tests ARE going to fail - soon and often. This is pain. And the problem.

有什么技巧可以解决这个问题吗?AutoMockingContainer(它基本上负责所有方法和嵌套的 DI 层次结构)看起来很有希望,但有其自身的缺点.还有什么?

Are there any techniques to deal with this? AutoMockingContainer (which basically takes care all ALL methods and nested DI hierarchies) looks promising, but with its own drawback. Anything else?

推荐答案

Dependency Injection 本身可以让您注入 IRepository 的实现,该实现接受对其进行的任何调用,检查是否满足不变量和前提条件,以及返回满足后置条件的结果.当你选择注入一个对调用什么方法有非常具体期望的模拟对象时,是的,你正在做高度特定于实现的测试——但是依赖注入在这件事上是完全无辜的,因为它从来没有决定你做什么应该注射;相反,您对 Mocking 的看法似乎与 Mocking 有关——事实上,特别是您选择使用的某种程度自动化的 Mocking 方法,这是一种基于非常具体的期望的方法.

Dependency Injection, per se, would let you inject an implementation of IRepository that accepts whatever calls are made on it, checks that the invariants and preconditions are satisfied, and returns results satisfying the postconditions. When you choose to inject a mock object that has very specific expectations for what methods will be called, then yes, you're doing highly implementation-specific testing -- but Dependency Injection is totally innocent in the matter, since it never dictates WHAT you should inject; rather, your beef appears to be with Mocking -- in fact, specifically the somewhat-automated mocking approach that you have chosen to use, which is one based on very specific expectations.

具有非常具体的期望的模拟确实仅对白盒测试有用.根据您使用的工具/框架/库(您甚至没有在标签中指定确切的编程语言,所以我假设您的问题是完全开放式的)您可以指定允许的自由度(这些调用可以以任何顺序出现,这些参数必须只满足以下先决条件等).但是,我不知道有什么自动化工具可以准确地执行不透明盒测试所需的内容,它是通用的、宽容的接口实现,具有所有需要的按合同编程"检查,没有其他".

Mocking with very specific expectations IS indeed useful for white-box testing only. Depending on the tools / frameworks / libraries you're using (and you're not even specifying the exact programming language in a tag, so I assume your question is totally open ended) you may be able to specify the degrees of freedom allowed (these calls are allowed to come in any orders, these arguments must only satisfy the following preconditions, etc, etc). However, I don't know of an automated tool to perform exactly what you need for opaque-box testing, which is the "generic, tolerant implementation of yonder interface with all the ''programming by contract'' checks that are needed and no other".

在项目的整个生命周期中,我倾向于为所需的主要接口构建一个不太模拟"的库.在某些情况下,这些可能从一开始就有些明显,但在其他情况下,当我考虑进行一些重大重构时,它们会逐渐出现,如下(典型场景)...:

What I tend to do over the life of a project is to build up a library of "not quite mocks" for the major interfaces needed. In some cases those may be somewhat obvious from the start, but in other cases they emerge incrementally as I'm considering some major refactoring, as follows (typical scenario)...:

重构的早期阶段打破了我最初廉价实施的脆弱的强烈期望嘲弄的某些方面,如果我认为这不是一次性的,我会考虑是只是调整期望还是全力以赴(即未来重构和测试的回报将证明投资是合理的)然后我手工编写一个很好的不太模拟"的代码并将其藏在项目的特定技巧包中——实际上通常可以在项目中重复使用;MockFilesystem、MockBigtable、MockDom、MockHttpClient、MockHttpServer 等类/包进入一个项目无关的存储库并被重用以测试各种未来的项目(实际上可能与公司内的其他团队共享,如果几个团队正在使用文件系统接口、bigtable 接口、DOM、http 客户端/服务器接口等,这些接口在团队之间是统一的.

The early stages of the refactoring break some aspect of the fragile strong-expectations mocking that I have cheaply put in place initially, I ponder whether to just tweak the expectations or go whole hog, if I decide it's not a one-off (i.e. the return in future refactorings and tests will justify the investment) then I hand-code a good "not quite mock" and stash it away in the project's specific bag of tricks -- actually often reusable across projects; such classes/packages as MockFilesystem, MockBigtable, MockDom, MockHttpClient, MockHttpServer, etc etc, go into a project-agnostic repository and get reused for testing all kinds of future projects (and in fact may be shared with other teams across the company, if several teams are using filesystem interfaces, bigtable interfaces, DOMs, http client/server interfaces, etc etc, that are uniform across the teams).

我承认,如果您将模拟"特指接口的用于测试目的的假实现"的精确预期风格,那么在这里使用模拟"一词可能有点不合适.也许 Stub、Shim、Fake、Test 或其他一些前缀可能更可取(出于历史原因,我确实倾向于使用 Mock,除非我记得特别将其称为 Fake 或类似名称;-).

I acknowledge that the use of the word "mock" may be slightly inappropriate here if you take "mock" to refer specifically to the precise-expectation style of "fake implementation for testing purposes" of interfaces. Maybe Stub, Shim, Fake, Test, or some other prefix yet might be preferable (I do tend to use Mock for historical reasons, except when I remember to specifically call it Fake or the like;-).

如果我使用的语言以清晰准确的方式用语言本身来表达界面中的各种按合同设计规范,我想我会获得对大多数情况的自动工具支持这种伪造/填充/等;但是我主要用其他语言编写代码,所以我必须在这里做更多的手动工作.但我认为这是一个单独的问题.

If I was using languages with clear and precise way to express in the language itself the various design-by-contract specs in an interface, I imagine I'd get automatic tool support for most of this faking/shimming/etc; however I mostly code in other languages so I have to do a bit more manual work here. But I think that's a separate issue.

这篇关于接口和单元测试——总是白盒测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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