b)数据处理流程和上层接口分开

  我们在安排源文件的时候,在安排函数分布的时候要注意一个基本的原则:数据和上层接口分开。在单元测试的时候,我们不太在乎曾经将这个数据的上层包装形式是什么。只有真正把数据从结构从释放出来,形成一个独立的处理文件,这样我们的测试才更方便、更有针对性。小函数、独立函数、与接口分离的函数,这些都是我们在代码开发中需要特别注意的。

  c)底层驱动打桩处理

  在真实的软件模块中,我们的代码是不可能独立存在的,因此当前模块的代码常常需要引用别的模块代码。建立符合自己模块的桩函数,一方面可以提高代码的开发效率,另外一方面也方便我们对自身的代码进行测试。当然,底层驱动打桩函数是多种多样的,某些配置类的函数我们可以象征地输出一行打印可以了;某些函数我们可以利用测试端的一个相似函数代替即可;另外本地不存在的一些函数可能还需要我们真正编写代码仿真一把。

  d)测试用例应该尽量和实际环境一致

  为了验证代码的正确性,编写测试用例当然是少不了的。但是,编写测试用例并不是说越多越好。重复、低质量的测试用例只会浪费我们的测试资源。那么,应该怎么做呢?其实真实的运行场景才是我们所关注的。对于我们来说,重要的是把那些基础功能、使用多的功能、容易犯错的功能设计成测试用例,剩下的测试用例才是关于覆盖率、性能方面的。

  e)重视代码覆盖率,更加重视功能覆盖率

  在开发中,很多开发者甚至领导都会把代码测试覆盖率当作单元测试很重要的一个条件。诚然,高的代码覆盖率固然能说明一些问题,但那不是问题的全部。我们进行单元测试的目的主要是为了验证功能实现和设计是否一致,不是为了测试而测试。当然,在测试中我们可以仿真很多的条件,90%甚至更高的代码覆盖率都是有可能的。但是,我们需要问一下自己,这些测试和后的功能测试关系很大吗?如果没有这些测试,会影响后的功能测试吗?我们假设的这些单元测试条件在实际运行的时候是真实存在的吗?

  f)多线程测试需要日志保存,在程序崩溃的时候生成dump文件

  有些功能的开发,是需要同时运行多个线程的。因此对于某些关键的配置,由于无法保证代码的执行顺序,我们需要对执行过程进行日志记录。如果程序在运行的过程中发生了崩溃,我们也需要及时对关键数据进行记录,保存系统生成的dump文件。

  g)测试用例注意生成环境和清除环境

  使用过CUnit的朋友想必对××_init和××_clean非常熟悉。××_init是为了给我们的测试用例构建测试环境,而××_clean则是对当前的测试环境进行清理,这样不至于对下面一个测试造成影响。本质上说,CUnit干的是这么一件事情,在测试用例运行前,自动调用你的**_init函数,执行结束后自动调用你的**_clean函数。如果你不使用现成的测试框架也没有关系,但是你在测试代码的时候也需要注意环境的生成和清理问题。

  h)测试代码也需要保存、重构、模块、分层设计

  测试代码也不是一层不变的,有的时候为了适应代码的重构需要,我们也需要对测试代码进行重构处理。比如说,有些代码我们是用来测试函数级别的基本功能的,有些代码我们是测试模块功能的,有的代码我们是测试函数性能的,这些测试代码都需要分开。另外,测试也会按照函数调用顺序不断增加测试的难度和复杂度的,所以测试代码的分层设计也是十分必要的。

  i)运行带测试用例的实际版本

  在版本release的时候,我们是绝不可能在实际版本中存在测试代码的。但是在开发的时候,我们可以自己编译生成带测试代码的版本。所以,我们需要做的是保证我们的测试代码不但可以在本地单元测试通过,还需要在实际环境通过。如果在这两方面都能通过的话,那么才能说明我们的测试是成功的,我们测试是有保障的,否则即使在本地做好了单元测试又有什么意义呢?