建议155:随出产代码一起提交单元测试代码
首先提出一个问题:我们害怕改削代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的呈报:新的版本,没有之前的版本不变,性能也更差了,Bug似乎也变多了。也就是说,重构的代码看上去质量更高了,可实际测试功效却不如人意。
几乎每个措施员都因为此类问题纠结过。我们要改削的代码也许来自某些不卖力任或经验欠佳的措施员,也许这些代码是本身一年前写的,但是看上去已经惨不忍睹。我们想要改削这些代码,却担忧重构出另外问题。即等于一个开发周期中的产品,也会有这样的选择呈现。某个模块可能已经提交测试并确认通过,不过此刻发明有更优的算法和逻辑,改还是不改,成了一个问题。
“单元测试”减轻甚至消除了开发者这种恐惧。如果项目没有测试代码,说明我们只是出产“按时炸弹”。很多人将出产代码和测试代码分袂看待,这是一种过时的做法。措施员在提交本身的出产代码时,必需同时提交本身的单元测试代码。很多现代化的版本打点工具可以在后台订制项目构建打算,自动运行测试项目,统计代码笼罩率,并生成相应呈报。我们应该在早上一边喝咖啡,一边读取这样的呈报。
有了测试代码做保证,在很洪流平上我们可以安心去重构了。如果某个成果偏离了既有成就,就会有夺目的提醒。
将单元测试放在首要职位地方的一种开发模式是TDD模式。TDD(Test Driven Development测试驱动开发)有三条严格的定律:
在编写不能通过测试的单元测试前,不要编写任何出产代码。
只编写刚好无法通过的单元测试,不能编译也算欠亨过。
只编写恰好足以通过当前掉败测试的出产代码。
即使我们的团队没有完全给与TDD的开发模式,也可以借鉴这些定律来编写我们本身的测试代码。我们无需一次性编写完全部的测试代码,那没有须要,这跟过度设计一样,,也不成能实现。事实上,我们应该逐地势编写测试代码,而且按如下法式来编写:
测试代码->出产代码->测试代码
下面编写一个单元测试的例子:
先编写一个Add要领:
public class SampleClass { public int Add(int a, int b) { return a + b; } }
右键创建单元测试:
VS会自动为我们生成一个测试要领:
/// <summary> ///Add 的测试 ///</summary> [TestMethod()] public void AddTest() { SampleClass target = new SampleClass(); // TODO: 初始化为适当的值 int a = 0; // TODO: 初始化为适当的值 int b = 0; // TODO: 初始化为适当的值 int expected = 0; // TODO: 初始化为适当的值 int actual; actual = target.Add(a, b); Assert.AreEqual(expected, actual); Assert.Inconclusive("验证此测试要领的正确性。"); }
将要领改削成我们需要的要领就可以了:
/// <summary> ///Add 的测试 ///</summary> [TestMethod()] public void AddTest() { SampleClass target = new SampleClass(); // TODO: 初始化为适当的值 int a = 1; // TODO: 初始化为适当的值 int b = 2; // TODO: 初始化为适当的值 int expected = 3; // TODO: 初始化为适当的值 int actual; actual = target.Add(a, b); Assert.AreEqual(expected, actual); Assert.Inconclusive("验证此测试要领的正确性。"); }
VS这个可视化测试工具太重量级了,导致开发的过程中运行测试代码太繁琐也太耗时。可以考虑用测试工具TestDriven.NET,这里不再介绍。
单元测试要注意一下几点:
首先,单元测试不应引入任何人机交互的内容。如,测试过程中不应该弹出对话框,期待用户输入或确认。单元测试不应该是被阻滞的。
其次,多线程也不属于单元测试领域,单元测试应该是快速被执行的,而不是需要期待的。
最后,单元测试不应该跨应用措施域,例如,数据访谒或者长途通信属于集成测试领域,而不是单元测试。