我知道所谓的单元测试和集成测试的教科书定义.我很好奇的是,什么时候编写单元测试...我会编写它们以涵盖尽可能多的类.
例如,如果我有一个Word
类,我会为Word
该类编写一些单元测试.然后,我开始写我的Sentence
班,当它需要与互动Word
类,我会经常写我的单元测试,从而测试它们都Sentence
和Word
......至少在他们互动的地方.
有这些测试基本上已经成为集成测试,因为他们现在测试这些2班的整合,还是仅仅是一个单元测试,跨越2班?
在一般情况下,因为这种不确定的线,我很少会实际编写集成测试......或者是使用我的成品,看看是否所有的碎片正常工作实际集成测试,即使他们是手动的,很少重复超出范围每个功能?
我是否误解了集成测试,或者集成和单元测试之间是否真的差别很小?
感谢所有人的响应!我认为从各种各样的答案中可以清楚地看出,单元测试和集成测试之间的界限肯定是模糊不清的,也许尝试找出哪些是真正的焦点应该保留在代码上有点迂腐(谢谢@Rob Cooper ).此外,抱歉,但我不会接受任何答案,因为太多太好了,而且看起来非常主观.
对我而言,关键的区别在于集成测试揭示了某个功能是否正常工作或被破坏,因为它们会在接近现实的场景中强调代码.他们调用一个或多个软件方法或功能,并测试它们是否按预期运行.
相反,测试单个方法的单元测试依赖于(通常是错误的)假设其他软件正常工作,因为它明确地模仿了每个依赖项.
因此,当实现一些功能的方法的单元测试是绿色的,它并不能意味着功能工作正常.
假设你有一个像这样的方法:
public SomeResults DoSomething(someInput) { var someResult = [Do your job with someInput]; Log.TrackTheFactYouDidYourJob(); return someResults; }
DoSomething
对您的客户来说非常重要:它是一项功能,唯一重要的功能.这就是为什么你经常写一个Cucumber规范断言它:你希望验证和沟通该功能是否有效.
Feature: To be able to do something In order to do something As someone I want the system to do this thing Scenario: A sample one Given this situation When I do something Then what I get is what I was expecting for
毫无疑问:如果测试通过,你可以断言你正在提供一个有效的功能.这就是您可以称之为商业价值的东西.
如果你想编写一个单元测试,DoSomething
你应该假装(使用一些模拟)其余的类和方法正在工作(即:方法正在使用的所有依赖项正确工作)并断言你的方法正在工作.
在实践中,你做的事情如下:
public SomeResults DoSomething(someInput) { var someResult = [Do your job with someInput]; FakeAlwaysWorkingLog.TrackTheFactYouDidYourJob(); // Using a mock Log return someResults; }
您可以使用依赖注入,或某些工厂方法或任何Mock Framework来执行此操作,或者只是扩展测试中的类.
假设有一个错误Log.DoSomething()
.幸运的是,Gherkin规范将找到它,您的端到端测试将失败.
该功能不起作用,因为它Log
被打破了,不是因为[Do your job with someInput]
没有做好自己的工作.顺便说一句,[Do your job with someInput]
这种方法是唯一的责任.
此外,假设Log
在100个其他类的100个其他方法中使用了100个其他功能.
是的,100个功能将失败.但是,幸运的是,100次端到端测试也失败了,并揭示了这个问题.而且,是的:他们说实话.
这是非常有用的信息:我知道我有一个破碎的产品.它也是非常令人困惑的信息:它没有告诉我问题在哪里.它告诉我症状,而不是根本原因.
然而,DoSomething
单位测试是绿色的,因为它使用的是假的Log
,永不打破.而且,是的:它显然在撒谎.它正在传达一个破碎的功能正在发挥作用.它有什么用?
(如果DoSomething()
单元测试失败,请确保:[Do your job with someInput]
有一些错误.)
假设这是一个类损坏的系统:
单个错误将破坏多个功能,并且几个集成测试将失败.
另一方面,同样的错误只会打破一个单元测试.
现在,比较两种情况.
同样的错误只会打破一个单元测试.
使用破碎的所有功能Log
都是红色的
您的所有单元测试都是绿色的,只有单元测试为Log
红色
实际上,使用损坏功能的所有模块的单元测试都是绿色的,因为通过使用模拟,它们会删除依赖项.换句话说,它们运行在一个理想的,完全虚构的世界中.这是隔离bug并寻找它们的唯一方法.单元测试意味着嘲笑.如果你不嘲笑,你不是单元测试.
集成测试告诉我什么不起作用.但他们猜测问题可能在哪里毫无用处.
单元测试是告诉你,唯一的测试,其中完全错误的.要绘制此信息,他们必须在模拟环境中运行该方法,其中所有其他依赖项应该正确工作.
这就是为什么我认为你的句子"或者它只是一个跨越两个班级的单元测试"在某种程度上取代了.单元测试不应该跨越2个类.
这个回复基本上是我在这里写的内容的摘要:单元测试谎言,这就是我爱他们的原因.
当我编写单元测试时,我通过模拟依赖项将正在测试的代码的范围限制为我当前正在编写的类.如果我正在写一个Sentence类,而Sentence依赖于Word,我将使用一个模拟Word.通过模拟Word我可以只关注它的界面并测试我的Sentence类的各种行为,因为它与Word的界面交互.这样我只测试Sentence的行为和实现,而不是同时测试Word的实现.
一旦我编写单元测试以确保Sentence在基于Word的界面与Word交互时表现正确,那么我编写集成测试以确保我对交互的假设是正确的.为此,我提供了实际的对象并编写了一个测试,该测试演练了一个最终同时使用Sentence和Word的功能.
我的10位:D
我总是被告知单元测试是对单个组件的测试- 应该充分发挥其作用.现在,这往往有很多层次,因为大多数组件都是由较小的部件组成.对我来说,单位是系统的功能部分.所以它必须提供一些有价值的东西(即不是字符串解析的方法,但也许是HtmlSanitizer).
集成测试是下一步,它采用一个或多个组件并确保它们按照应有的方式协同工作.然后,您就会担心组件如何单独工作,但是当您在HtmlEditControl中输入html时,它不知何故神奇地知道它的有效与否.
虽然它是一条真正可移动的线路.我宁愿更专注于让该死的代码完全停止^ _ ^
单元测试使用模拟
您正在谈论的是集成测试,它实际上测试了系统的整体集成.但是当你进行单元测试时,你应该分别测试每个单元.其他一切都应该被嘲笑.所以在你的Sentence
类的情况下,如果它使用Word
类,那么你的Word
类应该被嘲笑.这样,您将只测试您的Sentence
类功能.
我认为,当您开始考虑集成测试时,您更多地是在物理层而不是逻辑层之间进行交叉.
例如,如果您的测试涉及生成内容,则它是一个单元测试:如果您的测试只关注写入磁盘,它仍然是一个单元测试,但是一旦您测试了I/O和文件的内容,然后你自己进行集成测试.当您在服务中测试函数的输出时,它是一个单元测试,但是一旦您进行服务调用并查看函数结果是否相同,那么这就是集成测试.
从技术上讲,无论如何你都无法进行单级测试.如果您的班级由其他几个班级组成,该怎么办?这会自动使其成为集成测试吗?我不这么认为.
采用单一责任设计,其黑白两色.超过1个责任,它是一个集成测试.
通过鸭子测试(看起来,嘎嘎叫,蹒跚,它的鸭子),它只是一个单元测试,其中包含一个以上的新对象.
当你进入mvc并测试它时,控制器测试总是集成,因为控制器包含模型单元和视图单元.在该模型中测试逻辑,我会称之为单元测试.
甲单元测试模块的X是一个期望的测试(和用于检查)仅在模块X.问题
许多模块的集成测试是一种测试,它预计模块之间的协作会产生问题,因此单独使用单元测试很难找到这些问题.
按以下术语考虑测试的性质:
风险降低:这就是测试的目的.只有单元测试和集成测试的组合才能完全降低风险,因为一方面单元测试本身不能测试模块之间的正确交互,另一方面集成测试只能运行非平凡模块的功能在很小程度上.
测试编写工作:集成测试可以节省工作量,因为您可能不需要编写存根/假货/模拟.但是,在实现(和维护!)时,单元测试也可以节省工作量.这些存根/假货/模拟比没有它们的情况下配置测试设置更容易.
测试执行延迟:涉及重量级操作的集成测试(例如访问外部系统,如DB或远程服务器)往往很慢(呃).这意味着可以更频繁地执行单元测试,这可以减少调试工作(如果有任何失败),因为您可以更好地了解您在此期间所做的更改.如果您使用测试驱动开发(TDD),这一点就变得尤为重要.
调试工作:如果集成测试失败,但没有单元测试,那么这可能非常不方便,因为涉及的代码太多可能包含问题.这不是一个大问题,如果你之前已经改变只有几行-但是作为集成测试运行速度很慢,你或许也不会在如此短的时间间隔运行它们...
请记住,集成测试可能仍然存在/伪造/模拟其某些依赖项.这为单元测试和系统测试(最全面的集成测试,测试所有系统)提供了充足的中间地带.
因此,一种务实的方法是:灵活地依靠集成测试,尽可能明智地使用集成测试,并使用单元测试,这样做风险太大或不方便.这种思维方式可能比单元测试和集成测试的一些教条歧视更有用.
在我看来,答案是"为什么重要?"
是因为单元测试是你做的事情而集成测试是你不做的事情吗?或相反亦然?当然不是,你应该尝试两者兼顾.
是因为单元测试需要快速,隔离,可重复,自我验证和及时,并且集成测试不应该?当然不是,所有测试都应该是这些.
这是因为你在单元测试中使用模拟,但是你没有在集成测试中使用它们?当然不是.这意味着如果我有一个有用的集成测试,我不允许在某些部分添加模拟,我担心我必须将我的测试重命名为"单元测试"或将其交给另一个程序员来处理.
是因为单元测试测试一个单元并且集成测试测试了多个单元?当然不是.实际重要性是什么?无论如何,关于测试范围的理论讨论在实践中都会中断,因为术语"单元"完全依赖于上下文.在类级别,单元可能是一种方法.在程序集级别,单元可能是一个类,在服务级别,单元可能是一个组件.甚至类使用其他类,那么哪个单元?
这并不重要.
测试很重要,FIRST很重要,对定义的分析是浪费时间,只会让新手感到困惑.