测试驱动开发 测试前移
您对测试驱动开发的实用介绍
如今,您阅读了大量有关进行测试驱动开发(TDD)的所有优点的文章,并在技术会议上听到很多演讲,告诉您“进行测试!”以及执行这些测试有多酷。 你知道吗? 不幸的是,它们是正确的(不一定与“酷”部分有关,而与有用部分有关)。
测试是必须的 ! 在谈到TDD时,我们列出的典型优势是真实的:
- 您编写更好的软件
- 引入新功能后,您可以免受破坏
- 您的软件是自记录的
- 避免过度设计
即使我一直都同意这些优点, 但有时我还是以为我不需要TDD来编写优秀且可维护的软件。 当然,现在我知道我做错了,但是尽管职业选手闪闪发光,我为什么仍然有这个主意? 原因仅仅是一个。
![](https://i-blog.csdnimg.cn/blog_migrate/16166433c38f62824d21a66d59fdaf77.png)
花费很多
成本
花费很多! 可能有人在思考“ 但如果不进行测试,则成本甚至更高 ” —这也是正确的。 但是,这两个成本发生在不同的时间:
- 你做TDD➡现在就要花钱了 。
- 您不做TDD➡ 将来会付出代价的 。
那么,我们如何摆脱这种僵局呢?
完成某件事的最有效方法是尽可能自然地完成某件事。 人的本性是懒惰(这里的软件开发人员是表现最好的人)和贪婪的人,所以您现在必须找到降低成本的方法 。 这很容易说,但是很难做到!
在这里,我将分享我的经验以及为我带来利益/成本比率方面的成功经验。 但是在我这样做之前,让我们分析一下应用TDD的一些典型困难。
您可以测试两个数字的和吗?
一般来说,理论不是可选的。 您必须掌握它才能掌握练习。 但是,尝试一次应用您先前已获得的所有理论知识可能会产生以下效果:
![](https://i-blog.csdnimg.cn/blog_migrate/3a6ef855ac764bd8063fd9615f55780a.png)
您不能一次花费所有这些知识
关于TDD的典型理论课始于以下内容:
public class MathTests
{
[Test]
public void SumTest()
{
Math math = new Math();
Assert.AreEqual( 2 , math.Sum( 1 , 1 ));
}
}
public class Math
{
public int Sum( int x, int y)
{
return x + y;
}
}
在这里你就像
![](https://i-blog.csdnimg.cn/blog_migrate/7312a5f02a1dbace5904078c739a6090.png)
很简单,不是吗?
然后是这样的:
- 红色➡绿色➡重构周期
- 单元,验收,回归,集成测试
- 嘲笑,存根,假货
- 如果您很幸运(或者可能很不幸),有人会告诉您有关合同测试的信息,
- 如果您很幸运(或者可能很不幸),您将接触到遗留的代码库重构
事情变得艰难,但是您是一位经验丰富的开发人员,所有这些概念对您来说都不难处理。 然后,课程结束; 您回家,然后在接下来的几天里,您会认真地做一些代码修改以修正刚刚学到的概念。 到目前为止,一切都很好。
挣扎是真的
接下来是一个具有真实期限和真实计时成本的真实项目-但您有动力应用有光泽的新TDD。 您开始考虑软件的体系结构,并开始为第一类和该类本身编写测试-我们将其称为Class1
。
现在,您考虑Class1
的第一个用户,我们称它为UsageOfAClass
,然后再次测试并编写它。 Class1
是UsageOfAClass
的合作者,所以您要模拟它吗? 好吧,让我们模拟一下。 但是, Class1
和UsageOfAClass
真实交互又UsageOfAClass
呢? 也许您也应该对它们全部进行测试? 我们开始做吧。
在这一点上,你的内心,你开始听到一点声音,说:“ 我 会更快发展,如果我没有写这些测试...”。 您不会听这种邪恶的声音,而直接进行下一个测试。
UsageOfAClass
将使用Class2
,并将其自身UsageOfAClass
在Db中。 那么,我们是否必须测试Class2
,它与UsageOfAClass
交互以及在Db中的持久性? 但是等等……有人在TDD理论课上有没有提到如何应对I / O测试?
![](https://i-blog.csdnimg.cn/blog_migrate/8c29644f203814a66fe42b51482ffc7a.png)
好我辞职了
TDD背后的理论并不难理解,但如果您未采用正确的方法,将其应用于现实世界可能会非常复杂。
去做就对了
我们应该始终牢记,理论必须紧贴我们的需求,而不是相反。
主要目标是完成工作。 所以我的建议是, 随便做吧 !
从简单开始,直到完成任务。 然后,当您陷入某些理论思维循环时,例如:
这是一个单元测试还是一个集成测试?我应该在这里嘲笑还是不?哦,废话,这里我应该写一个新的协作者,所以一套全新的无限单元测试套件只是写“嘿,香蕉”……
暂时忘记理论,然后向前迈进。 随它来吧! 完成任务后,请回顾一下您的工作。 回顾完成的工作,将更容易分析什么是正确的事情。
实用TDD
去做就对了。 顺便说一句,我认为这也是解决TDD的正确方法。
我们如何构建Class1
, Class2
和UsageOfAClass
什么问题? 该方法。
这是一种自下而上的方法:
- 分析问题
- 弄清楚一种架构
- 从单元组件开始构建
这种方法是过度工程的最好朋友。 通常,您构建系统是为了防止您认为将来会发生更改,而又不知道它们是否确实会更改。 然后,当某些需求发生变化时,无论它有多好,通常都会以不适合您的结构的方式发生。
对我而言,大幅降低使用TDD进行写作的即时成本的关键在于采取自上而下的方法:
- 带来用户故事
- 写一个非常简单的用例测试
- 使其回到步骤2,直到完成所有用例
在执行此过程时,不必过分担心体系结构,干净的代码(嗯,至少要记住使用体面的变量名)或当前不需要的任何复杂形式。 尽一切所能 ,直到最后。
对故事的测试清楚地说明了当前和已知的要求。
完成后,请看一下意大利面泥浆代码的大丸子,摆脱耻辱,然后更深入地了解已完成的工作:
![](https://i-blog.csdnimg.cn/blog_migrate/671f8ce8776e2e2d46d807f77d993028.png)
看你做了什么
有用! 测试证明了这一点。所有系统都在那里, 而完成工作实际上需要什么 。
现在,您已经了解了系统的所有部分,因此您可以借助从头开始时没有的域知识来进行重构。 并且测试将确保重构时不会破坏任何内容。
重构
对我而言,开始重构的最佳方法是确定责任范围并将其划分为私有方法。 此步骤有助于确定职责及其输入和输出。
在那之后,协作者的类别几乎已经存在,您只需要将它们移动到不同的文件中即可。
在继续操作时,为从过程中弹出并迭代的类编写测试,直到对结果满意为止。 记住,如果您被卡在某个地方,那就去做吧! 如果您做的不好,一旦完成,您将获得更多有关如何在下次遇到错误时克服错误的信息。 尽您最大的能力,把工作做好是最重要的。
这样,如果您分析错误以从中学习,您还将提高自己的能力。
下一个用户故事
请按照以下步骤继续开发产品:
- 讲一个故事
- 使它完全在“测试-代码”循环中工作
- 重构
添加功能时,您将继续更改软件,甚至可能更改其结构。 但是随着系统的发展,由于TDD的两个主要功能,变更成本将保持线性增长:
- 架构发现(有助于控制复杂性)
- 保护免受重大更改
该系统不会进行过度工程设计,因为随着故事的完成,架构将会出现。 您无需考虑将来的需求; 如果最终需要它,则实施它的成本将很低。
有什么可能使它出错?
故事的大小。 最终构建的内容必须是正确的大小。 不太大(否则将花费太多时间来获得任何反馈)或太小(否则您将没有概述)。
如果故事太大了怎么办? 将其拆分为可以从头到尾构建的部分。
请分享您对本文的看法和建议。 您是否同意我的观点,或者您认为所有这些都是一堆垃圾? 让我知道您在评论中的想法; 最好在TDD上进行对话并分享我们的经验。
感谢您的阅读!
图片归功于 https://testsigma.com/blog/ai-driven-test-automation/
翻译自: https://hackernoon.com/a-practical-intro-to-test-driven-development-hb63i319u
测试驱动开发 测试前移