首页  编辑  

Delphi单元测试工具Dunit介绍

Tags: /超级猛料/IDE.集成开发环境/编译、调试/   Date Created:

Delphi单元测试工具Dunit介绍

广州邦讯科技 测试部 谢慧强

xiehuiqiang@21cn.com

Dunit 基本介绍

   Dunit Xunit 家族中的一员,用于 Dephi 的单元测试。是 Extreme Programming 测试实现 Xtreme Testing 的一种工具。 Dunit 是一个 Free 的测试工具,没有代码覆盖率功能。

Dunit 的官方 Web Site <https://sourceforge.net/projects/dunit/>

使用 Dunit 应该先看看 Dunit 安装目录下的 doc\README.html 。本文也是参看 Readme 写的。

配置测试环境

在使用 Dunit 前应该将下载的 Dunit 解压。然后后将 Dunit 的路径加到菜单  Tools->Environment Options 里面的 Library->Library Path 中。

Dunit 的主要文件

File Description
TestFramework.pas <D:dunit-5.0.11docAPIIDH_Unit_TestFramework.htm> The framework itself.
TestExtensions.pas <D:dunit-5.0.11docAPIIDH_Unit_TestExtensions.htm> Decorator classes that may be used to extend test cases.
GUITesting.pas <D:dunit-5.0.11docAPIIDH_Unit_GUITesting.htm> Classes for testing user interfaces (Forms and Dialogs).
TextTestRunner.pas <D:dunit-5.0.11docAPIIDH_Unit_TextTestRunner.htm> Routines to run tests in console mode.
GUITestRunner.pas <D:dunit-5.0.11docAPIIDH_Unit_GUITestRunner.htm> The graphical user interface to the framework..
GUITestRunner.dfm <D:dunit-5.0.11docAPIIDH_Unit_GUITestRunner.htm> The GUITestRunner Form

Dunit 基本实现方法( GUI 方式)

        Dunit 的基本实现思路是将被测试代码(单元)与测试代码(单元)分开。提供一个 FrameWork 及一个运行界面。 所有的测试单元都应继承 TtestCase

       运行 GUI 界面

  运行 TestCase

       这里要注意的一点是 SetUp 方法和 TearDown 是每个测试方法运行时都被调用的,如果想要只运行一次 Setup TearDown ,应该使用 TtestSetup 类,具体情况后面《 Dunit 附加功能》一节。

创建一个简单的例子

创建一个被测试的 Project

创建一个名为 BeTestProject Project, 将确省的 Unit1 保存为 BeTestUnit.pas 文件。把确省的 TForm1 改名为 BeTestForm 中增加一个 Public 的函数 BeTestFunction BeTestFunction 代码如下:

function BeTestForm.BeTestFunction(i,j:integer):integer;

begin

 Result:=i*j;

end;

创建一个测试 Project

创建新的 Project

再创建一个 Project ,命名为 TestProject 。如果没有和 BeTestProject 放在同一目录,将 BeTestProject 的存放路径加到加到菜单  Tools->Environment Options 里面的 Library->Library Path 中。

       

编写 TestCase

删除确省的 Unit1(Form1) ,创建一个的 Unit, 注意不是 Form.

将创建的 Unit 保存为 TestUnit ,在 interface 中加入以下代码

uses

     TestFrameWork,BeTestUnit;

   TestFrameWork 是每个 TestCase 都必须使用的,后面要使用的 TtestCase 等类的定义都在 TestFrameWork 中。 BeTestUnit 是将要被测试单元。

   

定义 TestCase ,测试类定义代码如下:

TTestCaseFirst = class(TTestCase)

private

  BeTestForm : TBeTestForm;  // 要测试的类

protected

  procedure SetUp; override;  // 初始化类

  procedure TearDown; override;  // 清除数据

published

  procedure TestFirst;      // 第一个测试方法

  procedure TestSecond;     // 第二个测试方法

end;

在定义测试方法时候注意, Dunit 是通过 RTTI(RunTime Type Information) 来寻找并自动注册测试方面的,具体实现是通过代码

TestFramework.RegisterTest(TTestCaseFirst.Suite);

这段代码将在后面提到, TtestCaseFirst.Suit 在寻找的规则是:

1 、        测试方法是没有参数的 Procedure

2 、        测试方法被申明为 Published

SetUp,TearDown 是在运行测试方法前、后运行的,所有一般把要测试的类的初始化及清除放在这两个过程中。

以下是实现的代码:

procedure TTestCaseFirst.SetUp;

begin

 BeTestForm := TBeTestForm.Create(Nil);

end;

procedure TTestCaseFirst.TearDown;

begin

 BeTestForm.Destroy;

end;

procedure TTestCaseFirst.TestFirst;      // 第一个测试方法

begin

 Check(BeTestForm.BeTestFunction(1,3) = 3,'First Test fail');

end;

procedure TTestCaseFirst.TestSecond;     // 第二个测试方法

begin

 Check(BeTestForm.BeTestFunction(1,3)=4,'Second Test fail');

end;

//Register TestCase

initialization

TestFramework.RegisterTest(TTestCaseFirst.Suite);

end.

Check TestCase 类提供的一个方法。以下是 TestCase 的实现代码:

procedure TTestCase.Check(condition :boolean; msg : string ); begin     if ( not condition) then         Fail(msg, CallerAddr); End ;

如果 Check 没有通过的话, Dunit 将报错。错误提示就在第二个参数中定义,其他有关类及方法的定义请看连机文档,文档放在

Dunit 安装目录\ doc\API\IDH_Library_DUnit_-_Xtreme_Unit_Testing_for_Delphi.htm

Initialzation 代码完成测试单元的注册。

修改 Project 主文件

       运行前的最后一步是修改 Project 主文件 TestProject.dpr 。先使用菜单 Project->View Source 打开 TestProject.dpr.

        修改后的代码如下:

program TestProject;

uses

 Forms,

 TestFrameWork,

 GUITestRunner,

 TestUnit in 'TestUnit.pas';

{$R *.res}

begin

 Application.Initialize;

 //Application.Run;

 GUITestRunner.RunRegisteredTests;

end.

上面的加粗代码是要增加和修改。

运行测试例子

  运行的测试结果如下:

       

使用 TestSuite

       使用 TestSuite 的目的是对 TestCase 进行分类管理,如果我们再增加一个 TestCase 如下

TTestCaseSecond = class(TTestCase)

published

  procedure TestThrid;

end;

添加 TestThrid 实现代码后,在 initialization 代码处增加

           TestFramework.RegisterTest(TTestCaseSecond.Suite);

运行以后我们可以看到结果如下:

  如果我们将 initialization 处的代码改为如下:

initialization

TestFramework.RegisterTest('Simple suite',TTestCaseFirst.Suite);

TestFramework.RegisterTest('Simple suite',TTestCaseSecond.Suite);

end.

      那么运行的结果如下:

          这就是一个简单的 TestSuite 的使用,我们将 TestCaseFirst TestCaseSecond 放到 Simple suite 中来进行管理。

  对于复杂的应用,我们也可以使用多层的 TestSuite 来进行管理。先增加一个函数:

function UnitTests: ITestSuite;

var

ATestSuite,BTestSuite: TTestSuite;

begin

 BTestSuite := TTestSuite.Create('Some trivial tests',

                        [

                         TTestCaseFirst.Suite,

                         TTestCaseSecond.Suite

                         ]);

                         

ATestSuite := TTestSuite.create('Some other  trivial tests');

ATestSuite.addTest(TTestCaseFirst.Suite);

ATestSuite.addTest(BTestSuite);

Result := ATestSuite;

end;

我们先使用 TtestSuite.Create 创建一个一层的 TestSuite, BtestSuite. 然后在将 BtestSuite 加入到 AtestSuite

最后将 initialization 处的代码改为如下:

initialization

TestFramework.RegisterTest('Simple Test', UnitTests);

end.

注册 AtestSuite 就可以了,以下是运行结果 :

控制台( console )模式

       如果想在 Dos 方式下直接运行 TestCase, 只要修改 Dpr 文件即可。

{ $APPTYPE CONSOLE}

program TestProject;

uses

Forms,

TestFrameWork,

GUITestRunner,

TextTestRunner,

TestUnit in 'TestUnit.pas';

{ $R *.res}

begin

Application.Initialize;

//  GUITestRunner.RunRegisteredTests;

TextTestRunner.RunRegisteredTests;

end.

先定义应用程序类型 , 加入{ $APPTYPE CONSOLE} ,然后使用 TextTestRunner 替代 GUITestRunner 就可以了。

  确省情况下,测试程序将把运行所有的 TestCase 后给出报告,如果想在达到一定错误就停止运行,可以使用

        TextTestRunner.RunRegisteredTests(rxbHaltOnFailures);

Dunit 附加功能

       使用 Dunit 的附加功能要先在 Uses 中加入:

          TestExtensions, // needed for TrepeatedTest

  Dunit 的主要附加功能有 :

1 、        重复运行某一 TestCase

2 、        使用 TtestSetup 类初试化

Dunit TestExtensions 还提到了两个类 TactiveTest TexceptionTestCase 来实现:

3 、        在独立线程中运行测试

4 、         Exception 测试

  但在 Dunit 中的最新源码,这两个类只是简单继承了 TtestDecorator 而没有做任何的修改,在 Dunit Readme 中也没有提到这两个类的用法。因此应该属于还没有实现的类。

重复运行 TestCase

       要重复运行某一 TestCase ,只需要将 initialization 里面的注册代码

TestFramework.RegisterTest(TTestCaseFirst.Suite);

  简单替换为:

TestFramework.RegisterTest(TRepeatedTest.Create(TTestCaseFirst.Suite, 2));

  就可以, TRepeatedTest.Create 的第一个参数为要重复的 TestSuite/TestCase, 第二个参数代表次数。运行后的结果如下:

       

       请注意, TestCaseFirst 前面多了" 2x"

使用 TtestSetup

       使用 TtestSetup 类的作用就是在运行所有的测试方法前后只运行一次 Setup TearDown 。可以用于创建数据库连接等等。

       要使用 TtestSetup, 我们先在《创建一个简单的例子》一节中创建的 TestUnit 中声明一个新的类(先在 Uses 中加入 Dialogs,TestExtensions

TestSetupTest = class (TTestSetup)

  protected

    procedure SetUp; override;  // 初始化类

    procedure TearDown; override;  // 清除数据

 end;

  加入实现代码

procedure TestSetupTest.SetUp;

begin

 ShowMessage('TestSetupTest Setup');

end;

procedure TestSetupTest.TearDown;

begin

 ShowMessage('TestSetupTest TearDown');

end;

  修改 TtestCaseFirst.SetUp TTestCaseFirst.TearDown, 加入下面加粗语句。

procedure TTestCaseFirst.SetUp;

begin

 BeTestForm := TBeTestForm.Create(Nil);

 ShowMessage('TTestCaseFirst Setup');

end;

procedure TTestCaseFirst.TearDown;

begin

 BeTestForm.Destroy;

 ShowMessage('TTestCaseFirst TearDown');

end;

最后将 initialization 改为

initialization

//TestFramework.RegisterTest(TTestCaseFirst.Suite);

TestFramework.RegisterTest(TestSetupTest.Create(TTestCaseFirst.Suite));

end.

运行之后的结果如下:

注意 TtestCaseFirst 前面加了 "[d]" 。运行一次测试就可以清楚看到 TestSetupTest 类中 Setup TearDown 只运行了一次,而 TtestCaseFirst 中的 Setup TearDown 运行了两次

测试 Exception

  虽然 TexceptionTestCase 没有实现,但是 Dunit 在源码附加\ examples\testexception 目录中有一个如何测试 Exception 的例子。

  主要的实现在 procedure TTestMyObject.CheckException procedure TTestMyObjectOverrideRunTest.RunTest 中。具体的实现可以看代码。

5396-dunit-7.2.0[1].zip (943.1KB)
img_12093.bmp (459.3KB)
img_13550.bmp (597.5KB)
img_19672.bmp (323.6KB)
img_27979.bmp (631.7KB)
img_29851.bmp (490.5KB)
img_4381.bmp (403.5KB)
img_5823.bmp (455.0KB)
img_8053.bmp (328.8KB)
img_8833.bmp (130.3KB)
img_9009.bmp (130.3KB)