测试驱动开发 测试前移

by Andrea Koutifaris

由Andrea Koutifaris

Test driven development has become popular over the last few years. Many programmers have tried this technique, failed, and concluded that TDD is not worth the effort it requires.

在过去的几年中,测试驱动的开发变得很流行。 许多程序员尝试了这种技术,但都失败了,并得出结论认为TDD不值得它付出努力。

Some programmers think that, in theory, it is a good practice, but that there is never enough time to really use TDD. And others think that it is basically a waste of time.

一些程序员认为,从理论上讲,这是一个好习惯,但是没有足够的时间真正使用TDD。 其他人则认为这基本上是浪费时间。

If you feel this way, I think you might not understand what TDD really is. (OK, the previous sentence was to catch your attention). There is a very good book on TDD, Test Driven Development: By Example, by Kent Beck, if you want to check it out and learn more.

如果您有这种感觉,我想您可能不了解TDD的真正含义。 (好的,前一句话是引起您的注意)。 如果您想了解一下并了解更多信息,那么肯特·贝克(Kent Beck)撰写了一本关于TDD的非常好的书,即“ 测试驱动开发:示例” 。

In this article I will go through the fundamentals of Test Driven Development, addressing common misconceptions about the TDD technique. This article is also the first of a number of articles I’m going to publish, all about Test Driven Development.

在本文中,我将介绍测试驱动开发的基础知识,以解决有关TDD技术的常见误解。 本文也是我将要发表的有关测试驱动开发的许多文章的第一篇。

为什么要使用TDD? (Why use TDD?)

There are studies, papers, and discussions about how effective TDD is. Even though it is definitely useful to have some numbers, I don’t think they answer the question of why we should use TDD in the first place.

关于TDD的有效性有研究,论文和讨论。 尽管有一些数字绝对有用,但我认为他们没有回答为什么我们应该首先使用TDD的问题。

Say that you are a web developer. You have just finished a small feature. Do you consider it enough to test this feature just by interacting manually with the browser? I don’t think it’s enough to rely just on tests done by developers manually. Unfortunately this means that part of the code is not good enough.

假设您是一名Web开发人员。 您刚刚完成了一个小功能。 您是否认为仅通过与浏览器手动交互就足以测试此功能? 我认为仅依靠开发人员手动进行的测试是不够的。 不幸的是,这意味着部分代码不够好。

But the consideration above is about testing, not TDD itself. So why TDD? The short answer is “because it is the simplest way to achieve both good quality code and good test coverage”.

但是上面的考虑是关于测试,而不是TDD本身。 那为什么要TDD? 简短的答案是“因为这是同时获得高质量代码和良好测试覆盖范围的最简单方法”。

The longer answer comes from what TDD really is… Let’s start with the rules.

更长的答案来自TDD的真正含义。让我们从规则开始。

游戏规则 (Rules of the game)

Uncle Bob describes TDD with three rules:

Bob叔叔用以下三个规则描述了TDD:

- You are not allowed to write any production code unless it is to make a failing unit test pass.

-除非要通过失败的单元测试,否则不允许编写任何生产代码。

- You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.

-不允许编写任何足以导致失败的单元测试; 编译失败就是失败。

- You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

-不允许编写任何足以通过一项失败的单元测试的生产代码。

I also like a shorter version, which I found here:

我也喜欢一个简短的版本,在这里可以找到:

- Write only enough of a unit test to fail.

-仅编写足够的单元测试以失败。

- Write only enough production code to make the failing unit test pass.

-仅编写足够的生产代码以使失败的单元测试通过。

These rules are simple, but people approaching TDD often violate one or more of them. I challenge you: can you write a small project following strictly these rules? By small project I mean something real, not just an example that requires like 50 lines of code.

这些规则很简单,但是接近TDD的人们经常违反其中一个或多个规则。 我向您挑战:您可以严格遵循这些规则来编写一个小项目吗? 在小型项目中,我指的是真实的东西,而不仅仅是一个需要50行代码的示例。

Those rules define the mechanics of TDD, but they are definitely not everything you need to know. In fact, the process of using TDD is often described as a Red/Green/Refactor cycle. Let’s see what it is about.

这些规则定义了TDD的机制,但绝对不是您需要了解的所有内容。 实际上,使用TDD的过程通常被描述为红色/绿色/重构周期。 让我们看看它的含义。

红色绿色重构周期 (Red Green Refactor cycle)

红相 (Red phase)

In the red phase, you have to write a test on a behavior that you are about to implement. Yes, I wrote behavior. The word “test” in Test Driven Development is misleading. We should have called it “Behavioral Driven Development“ in the first place. Yes, I know, some people argue that BDD is different from TDD, but I don’t know if I agree. So in my simplified definition, BDD = TDD.

在红色阶段,您必须针对即将实现的行为编写测试。 是的,我写了行为 。 测试驱动开发中的“测试”一词具有误导性。 我们应该首先将其称为“行为驱动的发展”。 是的,我知道,有些人认为BDD与TDD不同,但是我不知道我是否同意。 所以在我的简化定义中,BDD = TDD。

Here comes one common misconception: “First I write a class and a method (but no implementation), then I write a test to test that class method”. It actually doesn’t work this way.

这是一个常见的误解:“首先,我编写了一个类和一个方法(但没有实现),然后编写了一个测试以测试该类方法”。 实际上,这种方式行不通。

Let’s take a step back. Why does the first rule of TDD require that you write a test before you write any piece of production code? Are we TDD people maniacs?

让我们退后一步。 为什么TDD的第一条规则要求您在编写任何生产代码之前先编写测试? 我们是TDD人疯子吗?

Each phase of the R.G.R. cycle represents a phase in the code’s lifecycle and how you might relate to it.

RGR周期的每个阶段都代表代码生命周期中的一个阶段,以及您可能如何与之关联。

In the red phase, you act like you’re a demanding user who wants to use the code that’s about to be written in the simplest possible way. You have to write a test that uses a piece of code as if it were already implemented. Forget about the implementation! If, in this phase, you are thinking about how you are going to write the production code, you are doing it wrong!

在红色阶段,您的行为就像是一个要求苛刻的用户,希望使用将以最简单的方式编写的代码。 您必须编写一个使用一段代码的测试,就像已经实现了一样。 忘了执行! 如果在此阶段中您正在考虑如何编写生产代码,那么您做错了!

It is in this phase where you concentrate on writing a clean interface for future users. This is the phase where you design how your code will be used by clients.

在此阶段,您将专注于为将来的用户编写一个干净的界面。 这是您设计客户端如何使用代码的阶段。

This first rule is the most important one and it is the rule that makes TDD different from regular testing. You write a test so that you can then write production code. You don’t write a test to test your code.

第一条规则是最重要的规则,它是使TDD与常规测试不同的规则。 您编写测试,以便随后可以编写生产代码。 您无需编写测试来测试您的代码。

Let’s look at an example.

让我们来看一个例子。

// LeapYear.spec.jsdescribe('Leap year calculator', () => {  it('should consider 1996 as leap', () => {    expect(LeapYear.isLeap(1996)).toBe(true);  });});

The code above is an example of how a test might look in JavaScript, using the Jasmine testing framework. You don’t need to know Jasmine — it is enough to understand that it(...) is a test and expect(...).toBe(...) is a way to make Jasmine check if something is as expected.

上面的代码是使用Jasmine测试框架在JavaScript中进行测试的示例。 您不需要了解茉莉花-足以了解it(...)是测试并且expect(...).toBe(...)是使茉莉花检查某些东西是否符合预期的方法。

In the test above, I’ve checked that the function LeapYear.isLeap(...) returns true for the year 1996. You may think that 1996 is a magic number and is thus a bad practice. It is not. In test code, magic numbers are good, whereas in production code they should be avoided.

在上面的测试中,我检查了函数LeapYear.isLeap(...)在1996年返回true 。您可能认为1996是个神奇的数字,因此是一种不好的做法。 它不是。 在测试代​​码中,幻数是好的,而在生产代码中则应避免。

That test actually has some implications:

该测试实际上具有一些含义:

  • The name of the leap year calculator is LeapYear

    year年计算器的名称是LeapYear

  • isLeap(...)is a static method of LeapYear

    isLeap(...)LeapYear的静态方法

  • isLeap(...) takes a number (and not an array, for example) as an argument and returns true or false .

    isLeap(...)以数字(而不是数组)作为参数,并返回truefalse

It’s one test, but it actually has many implications! Do we need a method to tell if a year is a leap year, or do we need a method that returns a list of leap years between a start and end date? Are the name of the elements meaningful? These are the kinds of questions you have to keep in mind while writing tests in the Red phase.

这是一项测试,但实际上有很多含义! 我们是否需要一种方法来判断年份是否为a年,或者是否需要一种方法来返回开始日期和结束日期之间的of年列表? 元素的名称有意义吗? 在Red阶段编写测试时,您必须牢记这些问题。

In this phase, you have to make decisions about how the code will be used. You base this on what you really need at the moment and not on what you think may be needed.

在此阶段,您必须决定如何使用代码。 您基于当前真正需要的内容,而不是您认为可能需要的内容。

Here comes another mistake: do not write a bunch of functions/classes that you think you may need. Concentrate on the feature you are implementing and on what is really needed. Writing something the feature doesn’t require is over-engineering.

这是另一个错误:不要编写您认为可能需要的函数/类。 专注于您要实现的功能以及真正需要的功能。 编写功能不需要的东西是过度设计。

What about abstraction? Will see that later, in the refactor phase.

那抽象呢? 稍后会在重构阶段看到这一点。

绿相 (Green phase)

This is usually the easiest phase, because in this phase you write (production) code. If you are a programmer, you do that all the time.

这通常是最简单的阶段,因为在此阶段中您将编写(生产)代码。 如果您是一名程序员,那么您将一直这样做。

Here comes another big mistake: instead of writing enough code to pass the red test, you write all the algorithms. While doing this, you are probably thinking about what is the most performing implementation. No way!

这是另一个大错误:您没有编写足够的代码来通过红色测试,而是编写了所有算法。 在执行此操作时,您可能正在考虑什么是性能最高的实现。 没门!

In this phase, you need to act like a programmer who has one simple task: write a straightforward solution that makes the test pass (and makes the alarming red on the test report becomes a friendly green). In this phase, you are allowed to violate best practices and even duplicate code. Code duplication will be removed in the refactor phase.

在这一阶段,您需要像一名程序员一样扮演一个简单的任务:编写一个简单的解决方案,使测试通过(并使测试报告上的警报红色变为友好的绿色)。 在此阶段,您可以违反最佳做法,甚至可以重复代码。 代码重复将在重构阶段删除。

But why do we have this rule? Why can’t I write all the code that is already in my mind? For two reasons:

但是为什么要有这个规则? 为什么我不能编写所有已经想到的代码? 有两个原因:

  • A simple task is less prone to errors, and you want to minimize bugs.一个简单的任务不太容易出错,而您想减少错误。
  • You definitely don’t want to mix up code which is under testing with code that is not. You can write code that is not under testing (aka legacy), but the worst thing you can do is mixing up tested and untested code.您绝对不希望将正在测试的代码与没有测试的代码混合在一起。 您可以编写未经测试的代码(也称为旧版代码),但是最糟糕的事情是将经过测试和未经测试的代码混合在一起。

What about clean code? What about performance? What if writing code makes me discover a problem? What about doubts?

干净的代码呢? 性能如何? 如果编写代码使我发现问题怎么办? 怀疑呢?

Performance is a long story, and is out of the scope of this article. Let’s just say that performance tuning in this phase is, most of the time, premature optimization.

性能是一个很长的故事,超出了本文的范围。 可以说,在大多数情况下,此阶段的性能调整是过早的优化。

The test driven development technique provides two others things: a to-do list and the refactor phase.

测试驱动的开发技术提供了另外两件事:待办事项列表和重构阶段。

The refactor phase is used to clean up the code. The to-do list is used to write down the steps required to complete the feature you are implementing. It also contains doubts or problems you discover during the process. A possible to-do list for the leap year calculator could be:

重构阶段用于清理代码。 待办事项列表用于写下完成您正在实现的功能所需的步骤。 它还包含您在此过程中发现的疑问或问题。 year年计算器的可能工作清单可能是:

Feature: Every year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100, but these centurial years are leap years if they are exactly divisible by 400.
- divisible by 4- but not by 100- years divisible by 400 are leap anyway
What about leap years in Julian calendar? And years before Julian calendar?

The to-do list is live: it changes while you are coding and, ideally, at the end of the feature implementation it will be blank.

待办事项列表是实时的:您在编码时会更改,并且理想情况下,在功能实施结束时它将为空。

重构阶段 (Refactor phase)

In the refactor phase, you are allowed to change the code, while keeping all tests green, so that it becomes better. What “better” means is up to you. But there is something mandatory: you have to remove code duplication. Kent Becks suggests in his book that removing code duplication is all you need to do.

在重构阶段,您可以更改代码,同时使所有测试保持绿色,从而使其变得更好。 “更好”的意思取决于您。 但是有一些强制性的要求: 您必须删除代码重复 。 肯特·贝克斯(Kent Becks)在他的书中建议,消除代码重复是您所要做的全部。

In this phase you play the part of a picky programmer who wants to fix/refactor the code to bring it to a professional level. In the red phase, you’re showing off your skills to your users. But in the refactor phase, you’re showing off your skills to the programmers who will read your implementation.

在这个阶段,您扮演着一个挑剔的程序员的角色,他想修复/重构代码以使其达到专业水平。 在红色阶段,您向用户展示了自己的技能。 但是在重构阶段,您正在向将阅读您的实现的程序员展示您的技能。

Removing code duplication often results in abstraction. A typical example is when you move two pieces of similar code into a helper class that works for both the functions/classes where the code has been removed.

删除代码重复通常会导致抽象。 一个典型的例子是,当您将两个相似的代码片段移到一个帮助程序类中,该帮助程序类适用于已删除代码的两个函数/类。

For example the following code:

例如下面的代码:

class Hello {  greet() {    return new Promise((resolve) => {      setTimeout(()=>resolve('Hello'), 100);    });  }}class Random {  toss() {    return new Promise((resolve) => {      setTimeout(()=>resolve(Math.random()), 200);    });  }}new Hello().greet().then(result => console.log(result));new Random().toss().then(result => console.log(result));

could be refactored into:

可以重构为:

class Hello {  greet() {    return PromiseHelper.timeout(100).then(() => 'hello');  }}class Random {  toss() {    return PromiseHelper.timeout(200).then(() => Math.random());  }}class PromiseHelper {  static timeout(delay) {    return new Promise(resolve => setTimeout(resolve, delay));  }}const logResult = result => console.log(result);new Hello().greet().then(logResult);new Random().toss().then(logResult);

As you can see, in order to remove thenew Promise and setTimeout code duplication, I created a PromiseHelper.timeout(delay) method, which serves both Hello and Random classes.

如您所见,为了删除new PromisesetTimeout代码重复项,我创建了一个PromiseHelper.timeout(delay)方法,该方法同时提供Hello类和Random类。

Just keep in mind that you cannot move to another test unless you’ve removed all the code duplication.

请记住,除非您删除了所有重复的代码,否则您无法继续进行其他测试。

最后考虑 (Final considerations)

In this section I will try to answer to some common questions and misconceptions about Test Drive Development.

在本节中,我将尝试回答有关测试驱动开发的一些常见问题和误解。

  • T.D.D. requires much more time than “normal” programming!与“普通”编程相比,TDD需要更多的时间!

What actually requires a lot of time is learning/mastering TDD as well as understanding how to set up and use a testing environment. When you are familiar with the testing tools and the TDD technique, it actually doesn’t require more time. On the contrary, it helps keep a project as simple as possible and thus saves time.

实际上需要大量时间的是学习/掌握TDD以及了解如何设置和使用测试环境。 当您熟悉测试工具和TDD技术时,实际上并不需要更多时间。 相反,它有助于使项目尽可能简单,从而节省时间。

  • How many test do I have to write?我必须写多少个测试?

The minimum amount that lets you write all the production code. The minimum amount, because every test slows down refactoring (when you change production code, you have to fix all the failing tests). On the other hand, refactoring is much simpler and safer on code under tests.

使您可以编写所有生产代码的最小数量。 最小数量,因为每个测试都会减慢重构速度(更改生产代码时,必须修复所有失败的测试)。 另一方面,在测试中的代码重构更加简单和安全。

  • With Test Driven Development I don’t need to spend time on analysis and on designing the architecture.通过测试驱动开发,我不需要花时间在分析和设计体系结构上。

This cannot be more false. If what you are going to implement is not well-designed, at a certain point you will think “Ouch! I didn’t consider…”. And this means that you will have to delete production and test code. It is true that TDD helps with the “Just enough, just in time” recommendation of agile techniques, but it is definitely not a substitution for the analysis/design phase.

这不可能是错误的。 如果您要实施的方案设计不当,则在某些时候您会认为“好极了! 我没有考虑……”。 这意味着您将必须删除生产和测试代码。 TDD确实有助于敏捷技术的“足够,及时”建议,但绝对不能替代分析/设计阶段。

  • Should test coverage be 100%?测试覆盖率应该是100%吗?

No. As I said earlier, don’t mix up tested and untested code. But you can avoid using TDD on some parts of a project. For example I don’t test views (although a lot of frameworks make UI testing easy) because they are likely to change often. I also ensure that there is very a little logic inside views.

不,正如我之前说的,不要混淆经过测试和未经测试的代码。 但是您可以避免在项目的某些部分上使用TDD。 例如,我不测试视图(尽管许多框架使UI测试变得容易),因为它们可能经常更改。 我还确保视图内部有一点逻辑。

  • I am able to write code with very a few bugs, I don’t need testing.我能够编写带有几个错误的代码,不需要测试。

You may able to to that, but is the same consideration valid for all your team members? They will eventually modify your code and break it. It would be nice if you wrote tests so that a bug can be spotted immediately and not in production.

您也许可以做到这一点,但是对您所有团队成员而言,相同的考虑因素是否有效? 他们最终将修改您的代码并将其破坏。 如果您编写测试,以便可以立即发现错误而不是在生产环境中,那将是很好的。

  • TDD works well on examples, but in a real application a lot of the code is not testable.TDD在示例中效果很好,但是在实际应用中,很多代码是不可测试的。

I wrote a whole Tetris (as well as progressive web apps at work) using TDD. If you test first, code is clearly testable. It is more a matter of understanding how to mock dependencies and how to write simple but effective tests.

我使用TDD编写了完整的Tetris(以及工作中的渐进式Web应用程序)。 如果您先进行测试,则代码显然是可测试的。 更多的问题是了解如何模拟依赖关系以及如何编写简单而有效的测试。

  • Tests should not be written by the developers who write the code, they should be written by others, possibly QA people.测试不应由编写代码的开发人员编写,而应由其他人(可能是质量检查人员)编写。

If you are speaking about testing your application, yes it is a good idea to ask other people to test what your team did. If you are speaking about writing production code, then that’s the wrong approach.

如果您要谈论测试您的应用程序,是的,最好请其他人测试您的团队做了什么。 如果您要谈论编写生产代码,那是错误的方法。

下一步是什么? (What’s next?)

This article was about the philosophy and common misconceptions of TDD. I am planning to write other articles on TDD where you will see a lot of code and fewer words. If you are interested on how to develop Tetris using TDD, stay tuned!

本文介绍了TDD的哲学和常见误解。 我打算在TDD上写其他文章,您会看到很多代码和更少的单词。 如果您对如何使用TDD开发俄罗斯方块感兴趣,请继续关注!

翻译自: https://www.freecodecamp.org/news/test-driven-development-what-it-is-and-what-it-is-not-41fa6bca02a2/

测试驱动开发 测试前移

测试驱动开发 测试前移_测试驱动开发:它是什么,什么不是。相关推荐

  1. 测试驱动开发 测试前移_测试驱动的开发可能看起来是工作的两倍-但无论如何您都应该这样做...

    测试驱动开发 测试前移 by Navdeep Singh 通过Navdeep Singh 测试驱动的开发可能看起来是工作的两倍-但无论如何您都应该这样做 (Test-driven developmen ...

  2. 测试驱动开发 测试前移_测试驱动陷阱,第2部分

    测试驱动开发 测试前移 单元测试中单元的故事 在本文的上半部分 ,您可能会看到一些不好但很受欢迎的测试示例. 但是我不是一个专业的批评家(也被称为"巨魔"或"仇恨者&qu ...

  3. 测试驱动开发 测试前移_为什么测试驱动的开发有用?

    测试驱动开发 测试前移 有关如何更有效地应用TDD的技巧,以及为什么它是一种有价值的技术 (Tips on how to apply TDD more efficiently, and why it' ...

  4. 测试驱动开发 测试前移_测试驱动开发简介

    测试驱动开发 测试前移 I've been programming for five years and, honestly, I have avoided test-driven developme ...

  5. 测试驱动开发 测试前移_测试驱动开发–双赢策略

    测试驱动开发 测试前移 敏捷从业人员谈论测试驱动开发 (TDD),所以许多关心代码质量和可操作性的开发人员也是如此. 我曾几何时,不久前设法阅读了有关TDD的文章. 据我了解,TDD的关键是: 编写测 ...

  6. 测试驱动开发 测试前移_我如何以及为什么认为测试驱动开发值得我花时间

    测试驱动开发 测试前移 by Ronauli Silva 通过罗纳利·席尔瓦(Ronauli Silva) I first read about test driven development (TD ...

  7. python行为驱动测试开发_行为驱动开发在 Python 开发测试中的应用

    行为驱动开发 (BDD) 简介 行为驱动开发是什么? 说到行为驱动开发(BDD),无可避免的要提到敏捷里面的测试驱动开发(TDD),TDD 的主要思想是"代码即文档",其倡导的流程 ...

  8. 验收测试驱动开发包含哪四步_验收测试驱动开发

    验收测试驱动开发包含哪四步 在这个简短的关于验收测试的系列文章中,我之前写过关于测试人员和开发人员之间的协作以及验收测试如何帮助定义系统的明确要求的文章. 上一篇文章是理论上的,现在让我们看看在开发过 ...

  9. react测试组件_测试驱动的开发,功能和React组件

    react测试组件 This article is part of my studies on how to build sustainable and consistent software. In ...

最新文章

  1. python入门第二天__练习题
  2. silverlight bing maps sdk MapControl Sdk
  3. Java学习(三)面向对象异常
  4. 笨办法学R编程(1)
  5. lxml读取本地html文件,如何使用Python和lxml来解析本地html文件?
  6. R语言·文本挖掘︱Rwordseg/rJava两包的安装(安到吐血)
  7. 完成端口(IOCP)详解[1/2](转载)
  8. 【路径规划】基于matlab帝国企鹅算法求解机器人栅格地图避障路径规划问题【含Matlab源码 784期】
  9. Windows虚拟桌面
  10. 广义线性模型 matlab,基于Matlab的广义线性模型建模
  11. CNI插件之bridge plugin
  12. Python+Selenium_UI自动化操作(1)——将浏览器最大化
  13. bootstrap3中使用bootstrap-datetimepicker日期插件的用法
  14. linux mtd 块设备,基于块设备子系统的MTD子系统(2.6.26)
  15. SharePoint 2010 Webpart 部署 报错的解决方法
  16. 【SpringBoot】tk.mybatis集成,帮你更加傻瓜式的写代码~
  17. 西蒙菲莎大学计算机研究性硕士,不只是知名大学:西蒙菲莎大学你需要知道这些!...
  18. UNITY与Mac⭐三、Unity打包苹果应用的教程
  19. 【opencv】最近邻插值、双线性插值、双三次插值(三次样条插值)
  20. 解释什么是蓝绿发布?

热门文章

  1. 被面试官问的Android问题难倒了,面试必会
  2. 程序员35岁真的是分水岭吗?小白也能看明白
  3. python.day05
  4. iOS - UIScrollView
  5. 快速排序——算法系列
  6. 转:使用XMLSerializer类持久化数据
  7. Linux下GitLab的安装及使用
  8. Oracle RAC
  9. java spring cloud 版 b2b2c 社交电商-服务消费者(Feign)
  10. Spring Cloud Config服务端配置细节(一)