测试为先/测试驱动案例分析

发表于:2007-8-30 14:37

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Richard Sun    来源:Richard Sun的博客

        进行测试为先测试驱动的程序设计是确保敏捷开发顺进行的有效措施。这篇案例将为读者提供详细的开发历程,来分析测试为先测试驱动的程序设计的过程。本文的重点:

  • 简要重复叙述一下测试为先/测试驱动得好处。
  • 简要介绍一下案例中的项目。
  • 没有利用测试为先/测试驱动设计的单元代码是什么样的?
  • 没有利用测试为先/测试驱动设计的单元代码里有什么样的问题?
  • 针对单元代码设计的手动单元测试
  • 查找出毛病后的改进代码。

测试为先/测试驱动得好处
        传统的瀑布型软件开发是先从客户那里获得需求,然后进行纸上谈兵的设计,接着是程序源码写作构建,最后才是测试者对质量进行检评。从需求的分析到最后的测试,两者的相隔往往有好几个月。等到测试发现结构性问题时,重新设计已经成为一个无法完成的任务。设计者程序员已经无法回到几个月前推翻纸上谈兵的错误设计,重新用新的方式进行代码编写组合。测试为先/测试驱动和瀑布型软件开发不同是:

  • 测试模拟用户的使用组件的应用方式,为开发者提供解决方案;
  • 测试驱使开发者开发可以测试的部件;
  • 及早测试,尽快排除设计中的各种微小问题;
  • 测试为开发者提供质保底线,每次的部件更改都能利用测试来检测修改后质量。

        以上这些都是我以前重复过的。这些说的容易但是想像起来是比较难一点。我下面所要谈到的案例并不是教科书里的完美案例,所谓的完美案例是完全可以自动化,完全可以进行单元测试的程序组件。举个例子说,想像你要设计一个类来代表“复数”(imaginary number),这样一个类是可以完全进行自动化单元测试。这种情况只能算得上百分之五十的现实情况,在其他百分之五十的状况下,一些手动测试和一些自动化测试都是必要的。还有很多情况下,手动测试是唯一的选择。半自动和手动测试并不代表整个开发不算作测试驱动开发。测试驱动的多数人都会说手动测试和半自动化测试并不能代表团队在进行测试驱动的开法。我觉得这种说法是偏见,只要测试组和开发组能够配合,尽可能地在最早时间将用户需求确定后,让测试组开始针对用户需求,设计思路进行测试用例设计,开发和测试能同时进行,开发出的部件能够迅速进行测试,测试用例能够经常地运行确保开发的质量不受变化的影响。这就是测试为先/测试驱动的开发。

本文的案例简介
        用来演示测试为先/测试驱动的开发,我将使用我最近设计的一个将应用程序图标加入System Tray里的类。然后在应用程序退出后,自动将图标从System Tray里删除。这样的类,你如果知道Windows系统对System Tray里的图标管理,就知道设计这么一个类的自动化测试并不简单。我觉得这种和图形界面打交道的类,也没有必要100%地进行自动化测试。所以我对这个类的测试驱动采取手工测试为主的测试,以测试者甚至开法者本身用用户的需求,先用例程作为基础,来设计图标管理类的单元测试。

案例的用户需求
        我是这个类的唯一用户,对于我要设计的程序,我的使用是很简单的。下面的列表就是我的需求:

  • System Tray里的应用图标的数据管理和图标的加入删除都由类对象来进行;
  • 类对象能够设定视窗柄;
  • 类对象能够设定图标的独特ID;
  • 类对象能够设定图标对系统信息处理的消息ID;
  • 类对象能够设定图标在鼠标指向后能够显示有关程序的信息(程序的名称,设计公司和其他信息);
  • 类对象必须在调用者的指示下将图标放入System Tray。
  • 类对象必须处理让调试者能够删除System Tray里的图标。
  • 类对象在自我摧毁的时候自动删除System Tray里的图标。
        这些用户需求就是我要设计使用案例,在敏捷中,这些案例就是一个个故事。我在设计每一个故事的编码之前就先设计一个测试案例。每个测试案例都在设计完成之前会运行失败。设计完成后,这些测试案例才能顺利运行。

        为了调试图标的加入和删除都能正确运行,我决定使用一个简单的Win32视窗程序来作为我的单元测试温床,我的测试是手动测试。我的目标是用单元测试来尽可能地覆盖我设计的代码面积。第一步我设计了以下的单元测试:

void UnitTestCase0(HWND hWnd, HICON handleIcon)
{
   // a normal core functionality test.
   gSysTrayIcon.SetTrayIconID(11200);
   gSysTrayIcon.SetNotifyWindow(hWnd);
   gSysTrayIcon.SetTrayIcon(handleIcon);
   gSysTrayIcon.SetTrayIconTip(_T("SysTrayIcon"));
   gSysTrayIcon.SetTrayIconWmMsg(WM_TRAYICON_MSGS);
   if (!gSysTrayIcon.AddIconToSysTray())
   {
      ::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK);
      return;
   }
}
        这是一个很简单的函数,我假设我有一个全局变量叫gSysTrayIcon。它是一个类对象;它有至少六个函数;它的五个函数是数据设定函数;它的最后一个函数是让调用者告诉它把图像加入System Tray。根据我自己设计的单元测试案例,我设计了以下的类:
#ifndef SYS_TRAY_ICON_H_
#define SYS_TRAY_ICON_H_

#include "shellapi.h"

class SysTrayIcon
{
private:
   NOTIFYICONDATA niData;

public:
   SysTrayIcon();
   ~SysTrayIcon();

   void SetTrayIconID(UINT iconID);
   void SetNotifyWindow(HWND hWnd);
   void SetTrayIcon(HICON iconHandle);
   void SetTrayIconTip(LPCTSTR szMsg);
   void SetTrayIconWmMsg(UINT wmMsg);
   
   BOOL AddIconToSysTray();
   BOOL DeleteIconFromSysTray();

};

#endif

31/3123>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号