建议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 = ; // TODO: 初始化为适当的值
int b = ; // TODO: 初始化为适当的值
int expected = ; // 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,这里不再介绍。

单元测试要注意一下几点:

首先,单元测试不应引入任何人机交互的内容。如,测试过程中不应该弹出对话框,等待用户输入或确认。单元测试不应该是被阻滞的。

其次,多线程也不属于单元测试范畴,单元测试应该是快速被执行的,而不是需要等待的。

最后,单元测试不应该跨应用程序域,例如,数据访问或者远程通信属于集成测试范畴,而不是单元测试。

转自:《编写高质量代码改善C#程序的157个建议》陆敏技

编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码的更多相关文章

  1. 编写高质量代码改善C#程序的157个建议[1-3]

    原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...

  2. 读书--编写高质量代码 改善C#程序的157个建议

    最近读了陆敏技写的一本书<<编写高质量代码  改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...

  3. 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试

    建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...

  4. 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本

    建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...

  5. 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣

    建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...

  6. 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释

    建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...

  7. 编写高质量代码改善C#程序的157个建议——建议152:最少,甚至是不要注释

    建议152:最少,甚至是不要注释 以往,我们在代码中不写上几行注释,就会被认为是钟不负责任的态度.现在,这种观点正在改变.试想,如果我们所有的命名全部采用有意义的单词或词组,注释还有多少存在的价值. ...

  8. 编写高质量代码改善C#程序的157个建议——建议151:使用事件访问器替换公开的事件成员变量

    建议151:使用事件访问器替换公开的事件成员变量 事件访问器包含两部分内容:添加访问器和删除访问器.如果涉及公开的事件字段,应该始终使用事件访问器.代码如下所示: class SampleClass ...

  9. 编写高质量代码改善C#程序的157个建议——建议150:使用匿名方法、Lambda表达式代替方法

    建议150:使用匿名方法.Lambda表达式代替方法 方法体如果过小(如小于3行),专门为此定义一个方法就会显得过于繁琐.比如: static void SampeMethod() { List< ...

随机推荐

  1. canvas之画一个三角形

    <canvas id="canvas" width="500" height="500" style="background ...

  2. mysql where语句中 or 和 and连用注意点

    在mysql中,经常会遇到这样的情况,在写条件语句where时,可能会同时有多个条件的“或”或者“与”,但经常会达不到效果,经百度,本人发现一个where语句中同时出现条件的“与”或者“或的时候”,要 ...

  3. FastClick

    处理移动端click事件300毫秒延迟.FastClick 是一个简单,易于使用的js库用于消除在移动浏览器上触发click事件与一个物理Tap(敲击)之间的300延迟. 1.为什么会延迟? 从点击屏 ...

  4. NodeJS写模块和引入模块的例子

    nodejs自学.js function hello(){ console.log("hello world");} function s(){ console.log(" ...

  5. Julia - 循环

    while 循环 当 while 后的条件成立的话,执行循环体内的语句,直到条件不成立,跳出循环 如果条件一直成立,或者循环体中的语句没有能让条件不成立的,则是死循环 julia> i = 1; ...

  6. Python,OpenGL生命游戏

    初学Python和OpenGL,练手的第一个小程序life.py,这个小程序在日后会不断调整,增加类.优化判断及操作 执行效果: 按正规生命游戏的规则: 1.周围生命等于3时产生生命 2.周围生命等于 ...

  7. c++ 字符输入读取

    cin.clear()重置输入流 cin.get()锁住屏幕直到获取输入 while(cin) cin.get(ch) 方法返回的是一个cin对象,istream类提供了可以将istream对象转换为 ...

  8. makefile .phony targets

    Phony Targets PHONY 目标并非实际的文件名:只是在显式请求时执行命令的名字.有两种理由需要使用PHONY 目标:避免和同名文件冲突,改善性能. 如果编写一个规则,并不产生目标文件,则 ...

  9. c++builder 解压缩

    c++builder  解压缩  TZCompressionStream  TZDecompressionStream #include <System.ZLib.hpp> void __ ...

  10. beego 自定义模板函数

    beego支持的模板函数不是很多,有时候前端展现数据的时候,要对数据进行格式化,所以要用到自定义模板函数 比如我的前端模板上有时间和模板大小这2个数据,原始数据都是int的时间戳和byte单位的数据, ...