怎样做单元测试才有效(4)

发表于:2012-03-01来源:Csdn作者:xuyubotest点击数: 标签:单元测试
我们前面已经说过,代码通过编译只是验证它的语法通过。但并不能保证它的行为就一定正确。 6、 公司请我来是为了写代码,而不是写测试。 公司付给

  我们前面已经说过,代码通过编译只是验证它的语法通过。但并不能保证它的行为就一定正确。

  6、 公司请我来是为了写代码,而不是写测试。

  公司付给你薪水是为了让你编写产品代码,而单元测试大体上是一个工具,是一个和编辑器、开发环境、编译器等处于同一位置的工具。

  7、 如果我让测试员或者QA(Quality Assurance)人员没有工作,那么我会觉得很内疚。

  你并不需要担心这些。请记住,我们在此只是谈论单元测试,而它只是一种针对源码的、低层次的,为程序员而设计的测试。在整个项目中,还有其他的很多测试需要这些人来完成,如:功能测试验收测试性能测试、环境测试、有效性测试、正确性测试、正规分析等等。

  8、 我的公司并不会让我在真实系统中运行单元测试。

  我们所讨论的只是针对开发者的单元测试。也就是说,如果你可以在其他的环境下(例如在正式的产品系统中)运行这些测试的话,那么它们就不再是单元测试,而是其他类型的测试了。实际上,你可以在你的本机运行单元测试,使用你自己的数据库,或者使用mock 对象。

  测试代码编写

  多数讲述单元测试的文章都是以Java为例,本文以C++为例,后半部分所介绍的单元测试工具也只介绍C++单元测试工具。下面的示例代码的开发环境是VC6.0。

  产品类:

  class CMyClass

  {

  public:

  int Add(int i, int j);

  CMyClass();

  virtual ~CMyClass();

  private:

  int mAge; //年龄

  CString mPhase; //年龄阶段,如"少年","青年"

  };

  建立对应的测试类CMyClassTester,为了节约编幅,只列出源文件的代码:

  void CMyClassTester::CaseBegin()

  {

  //pObj是CMyClassTester类的成员变量,是被测试类的对象的指针,

  //为求简单,所有的测试类都可以用pObj命名被测试对象的指针。

  pObj = new CMyClass();

  }

  void CMyClassTester::CaseEnd()

  {

  delete pObj;

  }

  测试类的函数CaseBegin()和CaseEnd()建立和销毁被测试对象,每个测试用例的开头都要调用CaseBegin(),结尾都要调用CaseEnd()。

  接下来,我们建立示例的产品函数:

  int CMyClass::Add(int i, int j)

  {

  return i+j;

  }

  和对应的测试函数:

  void CMyClassTester::Add_int_int()

  {

  }

  把参数表作为函数名的一部分,这样当出现重载的被测试函数时,测试函数不会产生命名冲突。下面添加测试用例:

  void CMyClassTester::Add_int_int()

  {

  //第一个测试用例

  CaseBegin();{ //1

  int i = 0; //2

  int j = 0; //3

  int ret = pObj->Add(i, j); //4

  ASSERT(ret == 0); //5

  }CaseEnd(); //6

  }

  第1和第6行建立和销毁被测试对象,所加的{}是为了让每个测试用例的代码有一个独立的域,以便多个测试用例使用相同的变量名。

  第2和第3行是定义输入数据,第4行是调用被测试函数,这些容易理解,不作进一步解释。第5行是预期输出,它的特点是当实际输出与预期输出不同时自动报错,ASSERT是VC的断言宏,也可以使用其他类似功能的宏,使用测试工具进行单元测试时,可以使用该工具定义的断言宏。

  示例中的格式显得很不简洁,2、3、4、5行可以合写为一行:ASSERT(pObj->Add(0, 0) == 0);但这种不简洁的格式却是老纳极力推荐的,因为它一目了然,易于建立多个测试用例,并且具有很好的适应性,同时,也是极佳的代码文档,总之,老纳建议:输入数据和预期输出要自成一块。

  建立了第一个测试用例后,应编译并运行测试,以排除语法错误,然后,使用拷贝/修改的办法建立其他测试用例。由于各个测试用例之间的差别往往很小,通常只需修改一两个数据,拷贝/修改是建立多个测试用例的最快捷办法。

  测试用例

  下面说说测试用例、输入数据及预期输出。输入数据是测试用例的核心,老纳对输入数据的定义是:被测试函数所读取的外部数据及这些数据的初始值。外部数据是对于被测试函数来说的,实际上就是除了局部变量以外的其他数据,老纳把这些数据分为几类:参数、成员变量、全局变量、IO媒体。IO媒体是指文件、数据库或其他储存或传输数据的媒体,例如,被测试函数要从文件或数据库读取数据,那么,文件或数据库中的原始数据也属于输入数据。一个函数无论多复杂,都无非是对这几类数据的读取、计算和写入。预期输出是指:返回值及被测试函数所写入的外部数据的结果值。返回值就不用说了,被测试函数进行了写操作的参数(输出参数)、成员变量、全局变量、IO媒体,它们的预期的结果值都是预期输出。一个测试用例,就是设定输入数据,运行被测试函数,然后判断实际输出是否符合预期。下面举一个与成员变量有关的例子:

  产品函数:

  void CMyClass::Grow(int years)

  {

  mAge += years;

  if(mAge < 10)

  mPhase = "儿童";

  else if(mAge <20)

  mPhase = "少年";

  else if(mAge <45)

  mPhase = "青年";

  else if(mAge <60)

  mPhase = "中年";

  else

  mPhase = "老年";

  }

  测试函数中的一个测试用例:

  CaseBegin();{

  int years = 1;

  pObj->mAge = 8;

原文转自:http://www.ltesting.net