关闭

《.NET单元测试的艺术》读书笔记

标签: 单元测试的艺术 NUnit 桩 模拟对象
143人阅读 评论(0) 收藏 举报
分类:




单元测试基础知识 => NUnit框架 =>伪对象(桩,模拟对象)=>隔离框架(Rhino Mocks =>测试的层次组织 =>优秀的单元测试定义(可信赖,可维护,可读)=>测试的实施经验



基础知识

基本概念
单元测试定义:一段自动化的代码调用另一段代码,随后验证一些假设的正确性。单元测试几乎总是用单元测试框架来写的。它是全自动的,可信赖的,可读性强的,可维护的。
集成测试定义:把两个或者多个相依赖的软件模块作为一组进行测试。
单元测试和集成测试区别:
单元测试测的是一个单元,而集成测试测得是多个集成到一起的单元。
“回归”定义:以前好用的功能,现在却出现了问题。
TDD: 测试驱动开发。测试代码写在生产代码之前。
NUnit-单元测试框架;Rhino Mocks-创建桩对象和模拟对象的隔离框架。
NUnit基本知识
NUnit-.Net,免费开源; CppUnit-C++;JUnit-Java
单元测试命名规则:
测试项目 – [被测试项目].Tests 
测试类 – [被测类名]Tests
测试方法 – [被测方法名]_[测试场景]_[预期行为]。比如:IsValidFileName_validFile_ReturnsTrue().
NUnit中的特性:
[TestFixture]-用于标识NUnit的自动化测试类。
[Test]-标识NUnit的测试方法,表示一个需要被调用的自动化测试方法。
[SetUp]-应用于方法,NUnit在运行测试类中的每个测试(方法)之前都会执行SetUp方法。
[TearDown]-应用于方法,会在测试类中每个测试(方法)运行结束后,执行一次。
 
[TestFixtureSetup]-在一个指定测试类中的所有测试运行之前调用。
[TestFixtureTearDown]-在一个指定测试类中的所有测试运行结束后调用。
[ExpectedException]-用于测试异常。验证是否抛出。
[Ignore]-用于[Test]标记的测试方法上,忽略这个测试。在NUnit中显示为黄色。
[Category]-用于设定测试级别。完后在NUnit中选择运行特定的类别。
NUnit测试方法的返回类型必须是void,且参数为空。
NUnit框架中的Assert类中定义了一些断言静态方法。
比如Assert.IsTrue(),Assert.AreEqual()-期望的对象是否与实际的一样,Assert.AreSame()-两个参数引用的是否是同一个对象。
NUnit会自动检测测试程序集改变并自动重新加载。
使用NUnit步骤:
1. 创建测试项目[被测项目].Tests
2. 在类库中添加对被测项目的引用。
3. 添加NUnit.Framework程序集的引用。
4. 添加测试类[TestFixture]和测试方法[Test].
5. 在测试方法中调用被测对象,并使用Assert断言。(另外可加SetUp,TearDown等特性)。
6. 在NUnit图形界面中添加该测试程序集,开始测试。



核心技术


桩对象(Stub)
桩对象定义:系统中现有依赖项的一个替代品,可人为控制。通过使用桩对象,无需涉及依赖项,即可直接对代码进行测试。
接缝:指代码中可以插入不同功能(如桩对象类)的地方。
使用桩思想:a.找到被测对象对应的接口;b.将接口底层实现替换成自己可控的东西(策略模式)。
 

桩实现步骤:
a. 抽取接口。即桩和依赖对象都派生于该接口。
b. 被测试代码中使用接口。(桩是替换被测代码中的依赖项)。
c. 注入桩。即把桩对象传入被测试代码。
注入桩的三种方法(就是把抽取接口替换成桩实例):
a. 构造函数注入。
b. 使用抽取的接口类型的属性。
c. 在使用测试代码前通过工厂方法,工厂类等方式接收一个接口。
构造注入:
即在被测类的构造里面把抽取接口替换成桩实例。
 
如果增加一个桩,则需要增加一个构造函数,这是缺点,优点是表明注入是必选的必须指定的。
如果有多个桩,则可以把桩接口封装到类中,构造函数只传类,但这种做法可能会导致一个类里面有几十个属性。
属性注入:
即定义一个public属性来接收桩实例。
 

工厂类实现桩注入:
即在被测代码里面使用工厂类的静态方法返回的桩实例赋给抽取接口。在工厂类里如果是测试模式则返回桩实例,如果是发布模式则返回实际的依赖对象。
 

工厂方法实现桩注入:
即在被测试类中定义一个返回真实依赖对象的虚的工厂方法,而测试类派生于被测试类,重写该工厂方法,这个挺麻烦,参见P72.
 

使用internal和[InternalsVisibleTo]来使得测试代码仅对程序集可见。
使用[Conditional]和#if来使得测试代码不被包含到发布程序里。
模拟对象(Mock)
基于状态的测试:指在方法执行之后,通过检查被测系统及其协作者(依赖项)的状态来检测该方法是否正确工作。---结果驱动测试 --桩
交互测试:用来测试一个对象如何与其他对象进行交互。---动作驱动测试 –模拟对象
模拟对象定义:通过验证被测对象和模拟对象之间是否进行预期的交互来决定测试是通过还是失败。测试的是交互。通常每个测试只有一个伪对象。
桩对象和模拟对象的区别:

 
桩对象:断言针对被测类。桩对象不会使测试失败。一个测试项目内一般可以有多个桩对象。

 
模拟对象:断言针对模拟对象。模拟对象会使测试失败。一般一个测试项目只有一个模拟对象。
伪对象(fake object):是桩对象或者模拟对象的概称。
一个测试最好有且只有一个模拟对象,剩下的都是桩对象。一个测试中如果有多个模拟对象则意味着你在测多件事,这会导致测试变得复杂或脆弱。
隔离框架
隔离框架定义:创建模拟对象和桩对象的一组API框架。C++有Mockcpp, Java有jMock,。NET有NMock和Rhino Mocks.
Rhino Mocks:是一款免费的开源隔离框架。通过类MockRespository的方法来创建桩对象和模拟对象。有两种机制:录像-回放机制和设置-操作-断言机制。
Rhino Mocks创建动态模拟对象的一般步骤:
1. New一个MockRepository对象,调用其比如StrictMock()方法来创建运行时模拟对象。
2. 设置预期。
3. 注入模拟对象,调用测试代码。
4. 使用MockRepository的Verify或者VerifyAll方法来验证预期。
 


严格模拟对象:
只能被明确定义的预期方法所调用。也就是只能按照设置预期那样调用,任何参数,方法名不同,都会抛出异常。通过MockRepository.StrickMock<T>()来创建。
非严格模拟对象:
允许针对模拟对象的任何调用。仅当预期方法没有调用时才会导致测试失败,前提是要调用Verify,否则测试会通过。通过MockRepository.DynamicMock<T>()来创建。
从模拟对象返回值通过类LastCall.Return()来实现(最好不要这么用,用桩)。
模拟对象根据预先设置的输入值来返回结果。如果设置同样的输入,但设定的返回结果不同,则根据在代码中设计的顺序来返回。参见p112.
 

MockRepository.Verify和VerifyAll只适合模拟对象不适用桩对象,因为桩对象不会让测试失败。
Rhino Mocks创建桩对象:
MockRepository.GenerateStub<T>()
MockRepository.Stub<T>()
仍然使用LastCall.Return()为桩对象方法返回值。
 
 


但不能用Verify, VerifyAll.
Rhino Mocks中对桩对象和模拟对象的参数约束
即对桩对象和模拟对象中的函数参数进行约束。
1.使用LastCall.Contraints()方法+约束类(p121)来给桩对象和模拟对象添加参数约束:
 
2. LastCall.CallBack(回调函数)来使用回调函数检查参数。
Rhino Mocks模拟对象和桩对象触发事件:
 
测试一个事件是否被触发:
 


 

如果使用手写模拟对象就能满足需求,就不要使用隔离框架。




测试层次及组织



测试层次
集成测试和单元测试要分开
映射测试到类两种方式:
一个测试类对应一个被测类。
一个测试类对应一个功能。
测试类的继承模式:
1. 抽象测试架构模式:即把测试类共用的方法Setup和TearDown放到一个测试基类中:
 


2. 测试模板类模式:即建一个抽象的测试基类,包含派生测试类必须实现的抽象方法:
 
3. 抽象测试驱动类模式:新建一个抽象类,实现所有的测试方法,派生类只需默认继承这些方法,而不需要再次实现他们。
 
优秀的单元测试
优秀的单元测试要具有三个属性:可信赖性,可维护性,可读性
可信赖性:
1. 测试中不该有太多逻辑。
2. 只测一件事,不要有多个断言。如果有多个事要测,则拆成多个测试。实在要在一个测试中要测多个,则使用[RowTest]特性。
[RowTest]--参数化测试,一个[RowTest]失败,其他的仍会运行。而断言一个失败则后续的不会运行。P210,211.
3. 易运行,确保覆盖率。
可维护性:
1. 去除重复代码:工厂方法,Setup等。
2. 实施测试隔离:
a. NUnit不保证测试按照指定顺序执行
b. 不要在一个测试中调用另一个测试
c. 测试之间尽量不要共享状态,用单独的实例。
可读性:
1. 单元测试命名规则。参见前章节。
2. 尽量避免将断言和方法放在同一个语句中。
即不要在断言里有函数调用



测试实施(经验)


自下而上实施变革:
先从一个团队开始,获得一些成果,然后用证据去说服别人这些实践是有价值的。
自上而下实施变革:
说服管理层,从领导层开始实施,然后在组织里的其余部分逐步实施。
把代码覆盖率作为目标。
修改遗留的代码:
a. 从哪里开始测试:根据逻辑复杂度,依赖等级和优先级三个指标创建测试可行性表,进而画出示意图。
b. 根据示意图确定从哪开始测试:
从容易测试入手 --- 团队单元测试经验不住足。
从困难测试入手 --- 团队测试经验丰富。

也可以参考p258修改遗留代码工具。









0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

.NET单元测试艺术(奋斗的小鸟)_PDF 电子书

下载地址:http://pan.baidu.com/share/link?shareid=1048521129&uk=721744522   内容简介     《NET单元测试艺术》针对这个重要主题...
  • tjoy2005
  • tjoy2005
  • 2013-10-10 18:26
  • 1257

单元测试的艺术

本笔记主要来源于《单元测试的艺术》一书和本人些许经验 什么是单元测试 没有人没做过单元测试 public class SimpleTest {     public s...
  • RO_wsy
  • RO_wsy
  • 2015-12-09 11:29
  • 1293

Java加密与解密的艺术-读书笔记1-2章

作者-梁栋 第1章:企业应用安全 不管是哪一种IM工具,都在不遗余力地告诫用户聊天信息可能被盗取。 大批量、高负荷的数据交互时如何确定对方就是你所依赖的合作伙伴呢? 并不能保证手机平台就能比P...
  • u013695144
  • u013695144
  • 2015-08-08 16:48
  • 317

Android开发艺术探索读书笔记(一)

首先向各位严重推荐主席这本书《Android开发艺术探索》。     再感谢主席邀请写这篇读书笔记 + 书评。书已经完整的翻完一遍了,但是还没有细致的品读并run代码,最近有时间正好系统的把整本书从内...
  • amurocrash
  • amurocrash
  • 2015-09-24 18:34
  • 8086

《反欺骗的艺术》读后感

为了配合CSDN的读书送书活动,在收到此书后第一时间就翻阅起来,正好利用3天假期时间进行了阅读并写下这篇读后感,假期也因此变得充实起来。书的全名为《反欺骗的艺术——世界传奇黑客的经历分享》,原书的作者...
  • guwei4037
  • guwei4037
  • 2014-09-08 14:04
  • 3934

《清醒思考的艺术》读后感

咱们大部分人都是普通人,一些常见的思维漏洞我们也不免存在,这本书讲了50多种常见的常见的思维习惯并有小故事帮助我们理解这些习惯中的漏洞,让我们对事物有更加清楚的认识。 一、幸存偏误 概念:由于日常...
  • HEJI1103
  • HEJI1103
  • 2016-07-05 18:52
  • 1623

读书笔记:逆向思考的艺术

这本书的作者是Humphrey B. Neill,本来想查一下wiki看看这个人的介绍,居然没查到。逆向思考可以简单表述为:不落他人窠臼,相信自己的想法,思考问题的时候要尽量做到特立独行,避免思想的雷...
  • carolzhang8406
  • carolzhang8406
  • 2015-06-01 10:45
  • 1031

【我的JS第三本】JavaScript_DOM编程艺术第二版读书笔记

经过前一段时间HTML&CSS的学习,感觉视频加读书是一个比较不错的学习方法,两者相辅相成,互相补充,所以也准备看看关于JavaScript的书。       2015年12月14日,之前使用韩顺平老...
  • Creabine
  • Creabine
  • 2015-12-14 21:12
  • 3223

Android开发艺术探索读书笔记(二)-跨进程通信

在Androi系统中,为了对内存有一个统一的优化管理,通常对每个进程所能使用的最大内存做出限制。而在开发大型应用程序或者游戏的时候,为了获得更多的内存来支持程序的运行。往往需要在应用中开启多个线程来取...
  • dengminghli
  • dengminghli
  • 2017-02-05 22:04
  • 745

《LINUX内核设计的艺术》读书笔记(一)

前言
  • sadoshi
  • sadoshi
  • 2014-05-27 18:02
  • 1116
    个人资料
    • 访问:4949次
    • 积分:334
    • 等级:
    • 排名:千里之外
    • 原创:29篇
    • 转载:2篇
    • 译文:0篇
    • 评论:1条
    文章分类