一直熟知单元测试的重要性,也算是了看了几本这方面的经典书籍,但是真开始上手的时候总会遇到各种各样的坎。例如,为什么总感觉自己的单元测试之间有较多的重合,为什么每个单元测试都要准备那么多依赖?有的说法是,这意味着代码设计不够好,单元测试也有问题,或者说没有使用TDD的缘故,等等。但是,现实开发过程中在这方面也颇感无力。前段时间在微博上咨询了几个问题,感觉收获不大,这次干脆整理一份需求,仔细认真向高手学习一下代码设计,单元测试,甚至测试驱动开发的方式吧。我也会准备一些礼物来感谢一部分同学的帮助。

  MyDriver项目描述

  我准备了一个简单而完整的需求,使用C#和Java各实现了一份,以便更多同学可以加入到这次活动中来。不过下面的说明暂时围绕C#代码开展,后文也会提到Java实现与C#的区别,不过两者基本完全一致,只有些微不同,例如命名方式。

  首先,我们有一个MyDriver项目,您可以将其认为是一个网络服务的驱动程序。在实际情况下,这个驱动程序不会公开源代码,因此您不能修改这个项目里的任何代码,而只能在MyClient项目里使用它们。

  MyDriver项目里的核心类型便是MyDriverClient类,它有几个主要成员:

  ● MyDriverClient(string uri):构造函数,创建一个MyDriverClient类型的实例。uri为服务器地址,在这里只是个摆设。

  ● void Connect():连接服务器,连接后则可以进行后续操作。

  ● void AddQuery(int queryId):向服务器端发起一个查询,标识为queryId。使用相同queryId多次调用这个方法与调用一次无异。

  ● void RemoveQuery(int queryId):向服务器端取消一个查询,标识为queryId。如果这个查询本身并不存在,则不会发生任何事情。

  ● void Close():关闭与服务器端的连接。

  ● MyData Receive():接受一条服务器端返回的查询数据,如果没有数据,则会一直阻塞。如果连接关闭,则会返回null。

  其中Connect,AddQuery、RemoveQuery和Receive由于都要和服务器端进行通信,因此都有可能会抛出MyDriverException。这些方法一旦抛出异常之后,该MyDriverClient对象则需要被视为不可用,但我们依然需要调用Close方法将其关闭。

  MyData有两个字段,int类型的QueryID和字符串类型的Value。前者标识这条数据对应于哪条查询,而Value则是查询得到的数据,服务器端会将查询得到的数据不断推送到客户端。值得注意的是,即使使用了RemoveQuery取消了某个id的查询,但服务器端也可能已经向客户端推送了与这个id有关的数据,因此在一定时间内,Receive方法还是会得到这个id有关的MyData对象。这个时间虽然会很短,但并非不可能发生。

  在调用AddQuery方法添加某个id的查询之后,服务器端首先会推送一条{ QueryID: id, Value: "begin" }这样的MyData对象,表示查询已经生效,此后再会源源不断地推送与该查询相关的数据。

  在Program类型中有可执行的Main方法,可能会帮助您理解以上这些描述:

static voidMain(string[] args)
{vardriver =newMyDriverClient("jeffz://server:12345");try{
        driver.Connect();
        driver.AddQuery(1);
        driver.AddQuery(2);
        driver.AddQuery(3);
    }catch{
        driver.Close();Console.WriteLine("Error occurred when connect or add query.");Environment.Exit(1);
    }newThread(() => ReceiveData(driver)).Start();
}private static voidReceiveData(MyDriverClientdriver)
{try{while(true)
        {vardata = driver.Receive();if(data ==null)
            {Console.WriteLine("Closed");break;
            }else{Console.WriteLine(data);
            }
        }
    }catch(MyDriverException)
    {
        driver.Close();Console.WriteLine("Error occurred when receive data.");
    }
}