单元测试和自动化黑盒测试

目录

翻译内容

I finally have an answer(我终于得到了答案)

Umm, did you say you had an answer, dude? 嗯,你说你有答案,伙计?

What are the benefits? 有什么好处?

关于作者

原链接


翻译内容

If you’ve been following me from the beginning of the Back to Basics series, you’ll know that I set out to reevaluate some of the commonly held truths of what best practices are, especially in regards to unit testing, dependency injection and inversion of control containers.

在Back to Basics系列开始,如果你一直关注我,你就会知道我开始重新评估最常用的一些常见事实,特别是在单元测试,依赖注入和控制容器的反转方面。

We’ve talked about what an interface is, cohesion and coupling, and even went a little bit off track to talk about sorting for a bit.

我们已经谈到了接口是什么,内聚和耦合,甚至有点偏离轨道来谈论排序。

One of the reoccurring themes that kept showing up in most of the posts was unit testing.  I talked about why unit testing is hard, and I defined three levels of unit testing.

在大多数帖子中不断反复出现的主题之一是单元测试。 我谈论过为什么单元测试很难,我定义了四个级别的单元测试。

  • Level 1 – we have a single class with no external dependencies and no state.  We are just testing an algorithm.级别1  - 我们有一个没有外部依赖关系且没有状态的独立类。 我们只是在测试算法。
  • Level 2 – we have a single class with no external dependencies but it does have state.  We are setting up an object and testing it as a whole. 级别2  - 我们有一个没有外部依赖关系的类,但它确实有状态。 我们正在设置一个对象并将其作为一个整体进行测试。
  • Level 3 – we have a single class with at least one external dependency, but it does not depend on its own internal state. 级别3  - 我们有一个至少有一个外部依赖项的类,但它不依赖于自己的内部状态。
  • Level 4 – we have a single class with at least one external dependency and depends on its own internal state. 级别4 - 我们有一个至少有一个外部依赖的类,并依赖于它自己的内部状态。

Throughout this series I ended up tearing down using interfaces with only single non-unit test implementation.  I criticized the overuse of dependency injection for the sole purpose of unit testing.  I attacked a large portion of best practices that I felt were only really being used in order to be able to unit test classes in isolation.

在整个系列中,我最终拆除了仅使用单个非单元测试实现的接口。 我批评过为了单元测试,而过度使用依赖注入。 我攻击了大部分最佳实践,我觉得这些实践只是为了能够单独测试类。

But, I never offered a solution.  I told you what was bad, but I never told you what was good.

但是,我从未提供过解决方案。 我告诉你什么是坏事,但我从来没有告诉过你什么是好的。

I said don’t create all these extra interfaces, use IoC containers all over your app, and mocks everywhere just for the purpose of being able to isolate a class you want to unit test, but when you asked me what to do instead, I said “I don’t know, I just know what we are doing is wrong and we need to stop.”

我说不要创建所有这些额外的接口,在你的应用程序中使用IoC容器,并且为了能够隔离你想要进行单元测试的类而到处模拟,但是当你问我该怎么做时,我 说:“我不知道,我只知道我们做什么是错的,我们需要停下来。”

Well, that is no answer, but I intend to give one now.  I’ve been thinking about this for months, researching the topic and experimenting on my own.

嗯,这不是答案,但我打算现在给一个。 几个月来我一直在考虑这个问题,研究这个话题并亲自试验。

单元测试和自动化黑盒测试

I finally have an answer(我终于得到了答案)

But, before I give you it, I want to give you a little background on my position on the subject matter.

但是,在我给你之前,我想就你对这个主题的立场给你一点背景。

I come from a pretty solid background of unit testing and test driven development.  I have been preaching both for at least the last 7 years.

我有单元测试和测试驱动开发相当扎实的背景。 至少在过去的7年里,我一直在讲道。

I was on board from the beginning with dependency injection and IoC containers.  I had even rolled my own as a way to facilitate isolating dependencies for true unit tests.

我从一开始就使用依赖注入和IoC容器。 我甚至推出了自己的方法来帮助隔离真正的单元测试的依赖关系。

I think unit testing and TDD are very good skills to have.  I think everyone should learn them.  TDD truly helps you write object oriented code with small concentrated areas of responsibility.

我认为单元测试和TDD是非常好的技能。 我想每个人都应该学习它们。 TDD真正帮助您编写面向对象的代码,这些代码具有小而集中功能的区域。

But, after all this time I have finally concluded, for the most part, that unit tests and practicing TDD in general do more good for the coder than the software.

但是,经过这段时间我终于得出结论,在大多数情况下,单元测试和实践TDD通常对开发者更有用,而不是软件。

个人心得:同意作者的观点,从交付软件这个角度来说,单元测试、TDD的帮助并没有显而易见。

What?  How can I speak such blasphemy? 什么? 我怎么能说这样*的话?

The truth of the matter is that I have personally grown as a developer by learning and practicing TDD, which has lead me to build better software, but not because the unit tests themselves did much.

问题的真相是,我个人通过学习和实践TDD成长为开发人员,这导致我构建更好的软件,但不是因为单元测试本身做了很多。

What happened is that while I was feeling all that pain of creating mocks for dependencies and trying to unit test code after I had written it, I was learning to reduce dependencies and how to create proper abstractions.

当我感觉到为依赖创建模拟并尝试在编写代码之后对代码进行单元测试时的所有痛苦时,我正在学习减少依赖关系以及如何创建适当的抽象。

I feel like I learned the most when the IoC frameworks were the weakest, because I was forced to minimize dependencies for the pain of trying to create so many mocks or not being able to unit test a class in isolation at all.

我觉得当IoC框架最弱的时候我学到的最多,因为我*最大限度地减少依赖,因为试图创建这么多的模拟或根本无法单独测试一个类,是非常痛苦的。

I’ve gotten to the point now where two things have happened:

我现在已经发现了两件事:

  1. I don’t need the TDD training wheels anymore.  I don’t pretend to be a coding god or demi-god of some sort, but in general the code I write that is done in a TDD or BDD style is almost exactly the same as the code I write without it. 我不再需要TDD训练了。 我不假装是编码之神或某种类型的半神,但一般来说,我用TDD或BDD风格编写的代码几乎与没有它的代码完全相同。
  2. The IoC containers have made it so easy to pass 50 dependencies into my constructor that I am no longer feeling the pain that caused my unit tests to cause me to write better code. IoC容器使得将50个依赖项传递到我的构造函数中变得非常容易,以至于我不再感到痛苦了,它让我写我的单元测试,让我编写更好的代码。

What I find myself ending up with now when I write unit tests is 70% mocking code that verifies that my code calls certain methods in a certain order.

当我编写单元测试时,我发现自己现在最终得到的是70%的模拟代码,用于验证我的代码按特定顺序调用某些方法。

Many times I can’t even be sure if my unit test is actually testing what I think it is, because it is so complex.

很多时候我甚至无法确定,我的单元测试是否真的在测试我想要的东西,因为它太复杂了。

Umm, did you say you had an answer, dude? 嗯,你说你有答案,伙计?

Yes, I do have an answer.  I just wanted to make sure you understand where I am coming from before I throw out all these years of practical knowledge and good practices.

是的,我确实有答案。 在我抛弃所有这些年的实践知识和良好实践之前,我只是想确保你了解我的过往背景。

I am not the enemy.

我不是敌人。

My answer to the problem of what to do if you shouldn’t be using IoC containers and interfaces all over your code base just for the purpose of unit testing, is to take a two pronged approach.

如果您没有仅仅为了单元测试而在代码库中使用IoC容器和接口,那么我要解决的问题是采取双管齐下的方法。

单元测试和自动化黑盒测试

  1. Mostly only write level 1 or level 2 unit tests.  Occasionally write level 3 unit tests if you only have 1 or possibly 2 dependencies.  (I’ll talk about more how to do this in my next post) 通常只编写1级或2级单元测试。 如果您只有1个或2个依赖项,偶尔也会编写3级单元测试。 (我将在下一篇文章中讨论更多如何做到这一点)
  2. Spend a majority of your effort, all the time you would have spent writing unit tests, instead writing what I will call blackbox automated tests or BATs.  (I used to call this automated functional tests, but I think that name is too ambiguous.) 原本花费大部分精力,时间在编写单元测试上,相反,要编写我称之为blackbox自动化测试或BAT的东西。 (我以前称之为自动化功能测试,但我觉得这个名字太模糊了。)

I intend to drill really deep into these approaches in some upcoming posts, but I want to briefly talk about why I am suggesting these two things in place of traditional BDD or TDD approaches.

我打算在即将发布的帖子中深入探讨这些方法,但我想简要谈谈为什么我建议用这两种方法代替传统的BDD或TDD方法。

What are the benefits? 有什么好处?

The first obvious benefit is that you won’t be complicating your production code with complex frameworks for injecting dependencies and other clever things that really amount to making unit testing easier.

第一个显而易见的好处是,您不会使用复杂的框架来让生产代码复杂化,以便注入依赖和其他真正相当于使单元测试更容易的事物。

Again, I am not saying you shouldn’t ever use dependency injection, interfaces or IoC containers.  I am just saying you should use them when they provide a real tangible value (which most of the time is going to require alternate non-unit test implementations of an interface.)

同样,我并不是说你不应该使用依赖注入,接口或IoC容器。 我只是说你应该在它们提供真正价值时使用它们(大多数时候这将需要一个接口的非单元测试实现来替代。)

Think about how much simpler your code would be if you just went ahead and new’d up a concrete class when you needed it.  If you didn’t create an extra interface for it, and then pass it in the constructor.  You just used it where you needed it and that was that.

想想你的代码在你需要它的时候调用一个具体的类会变得多么简单。 如果没有为它创建额外的接口,然后在构造函数中传递它。 你只是在你需要的地方使用它,就是这样。

The second benefit is that you won’t spend so much time writing hard unit tests.  I know that when I am writing code for a feature I usually spend at least half the amount of time writing unit tests.  This is mostly because I am writing level 3 and level 4 unit tests, which require a large number of mocks.

第二个好处是你不会花太多时间编写困难的单元测试。 我知道当我为一个功能编写代码时,我通常花费至少一半的时间来编写单元测试。 这主要是因为我正在编写3级和4级单元测试,这需要大量的mock。

Mocks kill us.  Mocking has a negative ROI.  Not only is creating them expensive in terms of time, but it also strongly couples our test classes to the system and makes them very fragile.  Plus, mocking adds huge amounts of complexity to unit tests.  Mocking usually ends up causing our unit test code to become unreadable, which makes it almost worthless.

mock阻碍了我们。 模拟的投资回报率为负。 不仅创建它们在时间上昂贵,而且它还将我们的测试类强烈地耦合到系统中,并使它们非常脆弱。 此外,mock为单元测试增加了大量的复杂性。 mock通常导致我们的单元测试代码变得不可读,这使得它几乎一文不值。

I’ve been writing mocks for years.  I know just about every trick in the book.  I can show you how to do it in Java, in C#, even in C++.  It is always painful, even with auto-mocking libraries.

我多年来一直在写mock。 我知道书中的每一个技巧。 我可以向您展示如何在Java中,在C#中,甚至在C ++中实现。 即使使用自动mock库,它也总是很痛苦的。

By skipping the hard unit tests and finding smart ways to make more classes only require level 1 and level 2 unit tests, you are making your job a whole lot easier and maximizing on the activities that give you a high ROI.  Level 1 and level 2 unit tests, in my estimation, give very high ROIs.

通过跳过困难的单元测试,并找到明智的方法来让更多的类只需要1级和2级单元测试,您可以更轻松地完成工作,并最大限度地提高投资回报率。 在我的估计中,1级和2级单元测试给出了非常高的ROI。

The thirds benefit is that blackbox automated tests are the most valuable tests in your entire system and now you’ll be writing more of them.  There are many names for these tests, I am calling them BATs now, but basically this is what most companies call automation.  Unfortunately, most companies leave this job to a QA automation engineer instead of the development teams.  Don’t get me wrong, QA automation engineers are great, but there aren’t many of them, good ones are very expensive, and the responsibility shouldn’t lie squarely on their shoulders.

第三个好处是blackbox自动化测试是整个系统中最有价值的测试,现在你将能够编写更多这样的测试。 这些测试有很多名称,我现在称它们为BAT(blackbox automated tests),但基本上这就是大多数公司所说的自动化。 不幸的是,大多数公司将这项工作留给QA自动化工程师而不是开发团队。 不要误会我的意思,QA自动化工程师很棒,但是他们并不多,牛人很珍贵,责任也不应该直接落在他们的肩上。

BATs test the whole system working together.  BATs are your automated regression tests for the entire system.  BATs are automated customer acceptance tests and the ROI for each line for code in a BAT can be much higher than the ROI of each line of production code.

BAT测试整个系统可以集成起来工作。 BAT是您对整个系统的自动回归测试。 BAT是自动化的客户验收测试,BAT中每行代码的ROI可能远高于每行生产代码的ROI。

Why?  How is this even possible?  It’s all about leverage baby.  Each line of code in a BAT may be exercising anywhere from 5 to 500 lines of production code, which is quite the opposite case of a unit test where each line of unit test code might only be testing a 1/8th or 1/16th a line of production code on average (depending on code coverage numbers being reached.)

为什么? 这怎么可能呢? 这一切都与杠杆有关。 BAT中的每行代码可以在5到500行生产代码中运行,这与单元测试的情况完全相反,其中每行单元测试代码可能只平均测试一行生产代码的1/8或1/16 (取决于达到的代码覆盖率数量。)

I’ll save the detail for later posts, but it is my strong opinion that a majority of a development teams effort should be put in BATs, because BATs

我将在后续帖子中讲述详细信息,但我强烈认为,大多数开发团队的工作应该放在BAT中,因为BATs

  • Have high value to the customer 对客户有很高的价值
  • Regression test the entire system 回归测试整个系统
  • Have a huge ROI per line of code (if you create a proper BAT framework) 每行代码都有巨大的投资回报率(如果您创建了适当的BAT框架)

Imagine how much higher quality your software would be if you had a BAT for each backlog item in your system which you could run every single iteration of your development process.  Imagine how confident you would be in making changes to the system, knowing that you have an automated set of tests that will catch almost any break in functionality.

想象一下,如果系统中的每个积压项目都有BAT,那么您的软件质量会有多高,您可以在开发过程的每一次迭代中运行。 想象一下,您对系统进行更改时的信心会有多大,因为您知道自己拥有一组自动化测试,几乎可以捕获任何功能中断。

Don’t you think that is worth giving up writing level 3 and level 4 unit tests, which are already painful and not very fun to begin with to achieve?

难道你不认为放弃编写3级和4级单元测试是值得的吗?因为这些测试已经变得很痛苦,并且不是很有趣

In my future posts on the Back to Basics series, I will cover in-depth how to push more of your code into level 1 and level 2 unit tests by extracting logic out to separate classes that have no dependencies, and I will talk more about BATs, and how to get started and be successful using them.  (Hint: you need a good BAT framework.)

在我将来回归到基础知识系列文章时,我将深入介绍如何将更多代码实现1级和2级单元测试,方法是将逻辑提取出来,分离出没有依赖关系的类,我将详细介绍 BAT,以及如何开始并成功使用它们。(提示:你需要一个好的BAT框架。)

关于作者

John Sonmez is the founder of Simple Programmer and a life coach for software developers. He is the best selling author of the book "Soft Skills: The Software Developer's Life Manual."

John Sonmez是Simple Programmer的创始人,也是软件开发人员的生活教练。 他是“软技能:软件开发人员生活手册”一书的畅销书作者。

原链接

https://simpleprogrammer.com/back-to-basics-unit-testing-automated-blackbox-testing-and-conclusions/

上一篇:googletest--测试控制


下一篇:[笔记] Delphi使用DUnitX做单元测试的简单例子