我知道所谓的单元测试和集成测试的教科书定义。我好奇的是什么时候该写单元测试了……我将写它们来覆盖尽可能多的类集。

例如,如果我有一个Word类,我将为Word类编写一些单元测试。然后,我开始编写我的Sentence类,当它需要与Word类交互时,我经常会编写单元测试,这样它们就可以同时测试Sentence和Word……至少在他们相互作用的地方。

这些测试本质上变成集成测试了吗?因为它们现在测试这两个类的集成,还是仅仅是一个跨越两个类的单元测试?

一般来说,由于这条不确定的界线,我很少实际编写集成测试……或者我是否使用成品来查看所有部件是否正常工作,即实际的集成测试,即使它们是手动的,并且很少在每个单独的功能范围之外重复?

我是否误解了集成测试,或者集成测试和单元测试之间真的只有很小的区别?


当前回答

如果你是TDD的纯粹主义者,你会在写产品代码之前先写测试。当然,测试不能编译,所以您首先编译测试,然后让测试通过。

您可以在单元测试中这样做,但不能在集成测试或验收测试中这样做。如果您尝试进行集成测试,在您完成之前,什么都不会编译!

其他回答

我把那些白盒测试类的测试称为单元测试。类需要的任何依赖项都被替换为伪依赖项(mock)。

集成测试是同时测试多个类及其交互的测试。在这些情况下,只有一些依赖关系是伪造的。

我不会调用Controller的集成测试,除非它们的依赖项之一是真实的(即不是伪造的)(例如IFormsAuthentication)。

Separating the two types of tests is useful for testing the system at different levels. Also, integration tests tend to be long lived, and unit tests are supposed to be quick. The execution speed distinction means they're executed differently. In our dev processes, unit tests are run at check-in (which is fine cos they're super quick), and integration tests are run once/twice per day. I try and run integration tests as often as possible, but usually hitting the database/writing to files/making rpc's/etc slows.

这引出了另一个重要的问题,单元测试应该避免碰到IO(例如磁盘、网络、db)。否则他们会慢很多。设计这些IO依赖需要一些努力——我不能承认我一直忠实于“单元测试必须是快速的”规则,但如果你是,在一个更大的系统上的好处很快就会显现出来。

我的10位:D

我总是被告知单元测试是对单个组件的测试——应该充分地进行测试。现在,这往往有很多层次,因为大多数组件是由较小的部件组成的。对我来说,单元是系统的功能部分。所以它必须提供一些有价值的东西(例如,不是字符串解析的方法,而是HtmlSanitizer)。

集成测试是下一步,它采用一个或多个组件,并确保它们按照应有的方式一起工作。然后,你是在担心组件如何单独工作的复杂,但当你输入html到你的htmledcontrol,它以某种神奇的方式知道它是否有效。

这是一条真正可移动的线。我宁愿更专注于让该死的代码完全停止工作^_^

使用单一责任设计,它是黑白的。不止一个责任,它是一个集成测试。

通过鸭子测试(看起来,嘎嘎叫,摇摇摆摆,它是一只鸭子),它只是一个包含不止一个新对象的单元测试。

当你进入mvc并测试它时,控制器测试总是集成的,因为控制器包含一个模型单元和一个视图单元。在这个模型中测试逻辑,我称之为单元测试。

单元测试使用模拟

您正在讨论的是集成测试,它实际上测试了系统的整个集成。但是当你做单元测试时,你应该分别测试每个单元。其他一切都应该被嘲笑。在你的Sentence类的例子中,如果它使用了Word类,那么你的Word类应该被嘲笑。这样,您将只测试您的Sentence类功能。

类比的简单解释

上面的例子已经很好了,我就不再重复了。所以我会用例子来帮助你们理解。

集成测试

集成测试检查是否一切都在一起工作。想象一下手表上的一系列齿轮一起工作。一个综合测试是:手表是否显示正确的时间?它还能在3天内显示正确的时间吗?

它只告诉你整个部件是否正常工作。如果它失败了:它不会告诉你它到底在哪里失败。

单元测试

这些都是非常具体的测试类型。它们告诉你一件具体的事情是有效还是失败。这种类型的测试的关键是,它只测试一个特定的东西,同时假设其他一切都正常工作。这是关键。

例子: 让我们用一个例子来详细说明这一点:

Let’s take a car as an example. Integration test for a car: e.g. does the car drive to Woop Woop and back? If it does this, you can safely say that a car is working from an overall view point. It is an integration test. If it fails you have no idea where it is actually failing: is it the radiator, transmission, engine, or carburettor? You have no idea. It could be anything. Unit test for a car: Is the engine is working? This tests assumes that everything else in the car is working just fine. That way, if this particular unit test fails: you can be very confident that the problem lies in the engine – so you can quickly isolate and fix the problem.

使用存根

假设您的汽车集成测试失败。它不能成功地开到埃丘卡。问题出在哪里? 现在让我们假设你的发动机使用一个特殊的燃油喷射系统,这个发动机单元测试也失败了。换句话说,集成测试和发动机单元测试都失败了。那么问题在哪里呢?(给自己10秒钟的时间来回答。) 是发动机出了问题,还是燃油喷射系统出了问题?

你看到问题了吗?你不知道什么是失败。如果你使用不同的外部依赖关系,那么这10个依赖关系中的每一个都可能导致问题——你不知道从哪里开始。这就是为什么单元测试使用存根来假设其他一切都正常工作。