unit test pattern--1

来源:互联网 发布:拉拉肥捏脸数据 编辑:程序博客网 时间:2024/05/16 19:33

首先要说一下哪部分code一定要有unit test.
1. 依赖第三方接口的模块。因为第三方提供的结果不一定完全可信,数据可能会变化。如果有针对第三方接口的测试,当程序出现问题时,我们跑一下unit test, 就可以先判断出是第三方接口数据有变化还是自己的代码有问题。这样可以缩短debug的时间。
2. 模块之间的接口。和上面的原因一下,模块之间的接口是连接几个模块的唯一通道。通过对接口测试,也可以先把问题缩写在某个模块。

下面说一下可能在单元测试中需要注意的地方。
1. 子单元测试
想一想你有没有遇到过以下的问题。你的单元测试失败了,但是你要花费一段时间才能定位到错误的原因。为什么会这样,是因为unit test负责的功能太大了。这个时候需要把这一个unit test分成几个小的test进行测试。
我也会遇到这个问题。一个类它提供了public的接口。我会写unit test测试这个接口。但是这个接口可能是有三个函数实现的,比如A,B,C。所以当我发现unit test失败时,我不知道是A,B,C哪个错误导致的。那现在如果我们分别针对A,B,C也加了unit test进行测试,这样我们就会更容易地定位出错的地方。那如果在这里A可能是private的话,那A需要unit test为这个函数测试吗?
针对private(或protected)的函数是否需要测试的问题,有人说不应该对private 函数测试,因为private 函数之所以设置成private就是不想让外界访问,而且private 的函数可能更容易变化,这样导致unit test也要跟着变。也有人说如果需要可以对private 函数测试,就是因为如果不针对子函数进行测试的话,可能导致错误不容易定位的问题。对我来说,在某些情况下,还是会有对private函数进行测试的时候。
针对private,可以通过反射机制访问。针对protected的函数,我一般会让unit test的class继承被测试的类,这样在unit test的class中就可以访问到要测试的函数了。
2. Mock Object
我想写过unit test的人,几乎都写过Mock Object. Mock是模仿的意思,证明不是真正的对象。比如你要测试依赖数据库的逻辑是否正确,但是你真正去操作一个数据库,可能会对数据库的记录造成影响。或者你现在对一个文件进行操作,对文件进行操作你需要处理IO异常的情况。但是如果你读取的是一个真正的文件,你可能永远不知道处理IO异常的代码是否正确。这个时候你就可以用一个Mock的对象去模拟IO出现异常,看看你的程序是否能正常处理这个情况。
比如你定义一个class,它继承自StreamWriter(通过streamwriter访问文件).

 class MockStreamWriter : StreamWriter {     public override void Write(string value)     {         throw new IOException("");     } }

在unit test测试时,将需要StreamWriter的时候,传入MockStreamWriter的对象,这样当调用Write方法时,就调用了MockStreamWriter的Write方法,这个时候你就可以测试你的代码能否成功handle IOException.
3. Self Shunt
Self Shunt就是说我们可以把unit test自己作为Mock Object, 而不需要额外再创建一个class. 比如现在有一个class Person, 这个class有很多Listener, 只要Person中的Notify执行,就会把object的状态通知Listener.

 class Person {       //other methods and fields....      public IList<Listener> ListenerList;      public void Notify()      {           for (int i=0; i<ListernList.count; i++)           {               ListenerList[i].Accept(this);           }       }        }

正常测试时,我们需要Mock Listener, 它继承自Listener.

class MockListener : Listener{    public int Count = 0;    public override void Accept(Person p)    {        Count++;    }}

Unit Test:

public void Test(){    Person p = new Person();    MockListener listener = new MockListener();    p.ListenerList.Add(listener).    p.Notify();    Assert.AreEqual(1, listener.Count);}

现在我们让Unit Test的class再增加一个功能,即作为MockListener.

class MockListener_UnitTest : Listener{    public int Count = 0;    public override void Accept(Person p)    {        Count++;    }    public void Test()    {        Person p = new Person();        p.ListenerList.Add(this).        p.Notify();        Assert.AreEqual(1, this.Count);    }}

这样做的好处是你可以不用再写一个额外的class了。但是有可能你需要实现Listener所有的接口(可以返回一个合理的值或者抛出异常)。把Unit Test本身作为Mock object, 代码读起来更方便。

0 0
原创粉丝点击