单元测试(Unit Testing)基础

来源:互联网 发布:房地产数据分析图片 编辑:程序博客网 时间:2024/04/26 16:03

作者

Richard Paul
Kiwiplan NZ Ltd
27 Feb 2009

单元测试是什么?

在计算机编程里,单元测试是一种用来验证单独的代码是否正确的方法

http://en.wikipedia.org/wiki/Unit_testing

单元测试带来的好处

  • 自动化的回归测试,保证代码持续地正常
  • 作为代码的一种可运行的,一直保持更新的文档
  • 让代码更加稳定,使人能放心重构
  • 能与系统的其它部分分开进行测试,并且其它系统部分可能还不存在。
  • 更少的BUG!

单元测试与功能测试

单元测试是从程序员的观点来写的,他们保证特定的类或者方法正常地执行一系列指定的任务。

功能测试是由用户的观点而来的, 这些测试确保系统是按用户的需求来运行的。

http://www.ibm.com/developerworks/library/j-test.html

单元测试的结构

  • 一个用例一个测试方法
  • 测试方法互相独立地运行
public class ItemControllerTest {    private ItemController itemController;    @Mock    private ItemService itemService;    private Map<String, Object> modelMap;    @Before    public void setup(){      itemController = new ItemController(itemService);      modelMap = new HashMap<String, Object> ();   }   @Test   public void testViewItem() throws Exception {      Item item = new Item(1, "Item 1");      when(ItemService.getItem(item.getId())).thenReturn(item);      String view = itemController.viewItem(item.getId(), modelMap);      assertEquals(item, modelMap.get("item"));      assertEquals("viewItem", view);}


单元测试最佳实践

单元测试应该运行得快

  • 允许程序员快速地编程
  • 除非真正需要,不要碰到数据库

测试应该只断言其需要的场景

  • 包含太多不必要的测试会带来过高的维护

单元测试应与代码同时编写

单元测试应该在代码提交时自动运行

  • 使用持续集成的服务器,比如Hudson, Jenkins.
  • 快速反馈集成时出现的问题

测试驱动开发

  1. 添加一个基础测试
  2. 编写刚好足够通过测试的代码
  3. 添加另一个测试,重复第二步

测试驱动的优势

  • 鼓励你考虑如何使用你的对象
  • 开发更快,不需要编译和部署项目来确定一段代码可以运行
  • 调试一段单元测试要比调试一个部署好的应用程序要容易得多
  • 一系列的回归测试让你重构代码的时候不用担心破坏已有的功能
  • 单元测试可以覆盖更多的边缘测试情景,因为作为程度员你更熟悉对象的模型

显示接缝(Seam)

  • 接缝是指被测试的代码与其它组件隔离开的分割线
  • 与接缝另一面组件的合作应该是可替换的,以确保我们可以隔离想要测试的代码。
  • 能表现出接缝的代码可以认为是低耦合的

应用接缝

  • 在实际运行中,使用的都是实际的对象。
  • 代码有接缝:可以使用模拟的对象(Mock)来替换实际的其它组件进行测试。
  • 反模式-静态代码:无接缝,无法替换代码中与其它组件的合作部分,导致复杂冗长的测试代码。
  • 本地创建合作对象:无接缝,被测试的类自己创建合作的其它类的对象,我们无法替换这些对象。

反模式(Anti Panttern)更多资料

Static Methods are Death to Testability
http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/
To "new" or not to "new"…
http://misko.hevery.com/2008/09/30/to-new-or-not-to-new/

Mocking框架

Mocking框架提供了便利的方法来来”仿造”其它组件的对象,使得我们能专心测试想要的代码。

这些框架包括

Java

  • Mockito
  • EasyMock
  • JMock
  • .Net

.Net

  • Moq (需要.Net 3.5)
  • Rhino
  • TypeMock

实例 - 访问一个合作对象

为了给使用其它服务类的代码提供一个接缝,需要把这些服务类替换出去。

依赖注入

Java

  • Spring框架
  • Guice

.Net

  • SpringFramework.Net
  • Castle MicroKernel/Windsor

服务访问

Poor mans dependency injection

实例 -  依赖注入

资源被注入进需要他们的类中

构造器注入

public class ItemController {  private ItemService itemService;  public ItemController(ItemService itemService)  {     this.itemService = itemservice; }...

Setter注入

public class ItemController {  private ItemService itemService;  public ItemController () {}  public void setItemService(ItemService itemService) {    this.itemService = itemService; }...

 

实例 - 服务访问

如果你没有依赖注入容器,使用单例模式也对测试有好处

public class ServiceAccess {  private ItemService itemService;    public static ItemService getItemService() {    return itemService;  }  public void setItemService(ItemService itemService) {    ServiceAccess.itemService = itemService; }}


在你的测试里,可以把itemService变成Mock版本,这样就能有接缝了

实例 - 环境

Java : Eclipse, JUnit 4.5, Mockito 2.7

VB.net: Visual Studio 2005, NUnit 2.4, Rhino Mocks 3.5

更多Java相关的例子(Mockito)请参考

http://www.rapaul.com/2008/11/19/mocking-in-java-withmockito/

测试覆盖率

让测试覆盖率亮绿灯

Java - EMMA, 有Eclipse 插件EclEmma

.Net - NCover, 有Visual Studio支持

总结

  • 每个测试用例应该明确地定义在单独的测试方法里
  • 单元测试提供阐述代码如何工作的能运行的文档
  • 测试的可维护很重要,因为测试代码占代码的比重很大

              - 记录下单元测试覆盖的用例

              - 过度地定义单元测试会导致更多的维护量

  • 制造接缝使得代码之前耦合低,并且通过Mock对象能让单元测试变得简单

测试驱动开发注重代码是如何使用的,而且降低了编译和部署的周期 = 更快

原创粉丝点击