我正在使用DUnit测试一个Delphi库。有时我会遇到这样的情况,我编写了几个非常相似的测试来检查一个函数的多个输入。
有没有一种方法可以在DUnit中编写(类似于)参数化测试?例如,为适当的测试过程指定输入和预期输出,然后运行测试套件并获得关于测试多次运行中哪一次失败的反馈?
(编辑:一个例子)
例如,假设我有两个这样的测试:
procedure TestMyCode_WithInput2_Returns4();
var
Sut: TMyClass;
Result: Integer;
begin
// Arrange:
Sut := TMyClass.Create;
// Act:
Result := sut.DoStuff(2);
// Assert
CheckEquals(4, Result);
end;
procedure TestMyCode_WithInput3_Returns9();
var
Sut: TMyClass;
Result: Integer;
begin
// Arrange:
Sut := TMyClass.Create;
// Act:
Result := sut.DoStuff(3);
// Assert
CheckEquals(9, Result);
end;我可能会有更多这样的测试,它们都是完全相同的,但是有不同的输入和期望。我不想把它们合并成一个测试,因为我希望它们能够独立通过或失败。
发布于 2012-01-25 16:56:49
您可以使用DSharp来改进DUnit测试。特别是新的单位DSharp.Testing.DUnit.pas (在德尔菲2010及以上)。
只需在TestFramework之后将其添加到您的使用中,就可以将属性添加到测试用例中。它看起来可能是这样的:
unit MyClassTests;
interface
uses
MyClass,
TestFramework,
DSharp.Testing.DUnit;
type
TMyClassTest = class(TTestCase)
private
FSut: TMyClass;
protected
procedure SetUp; override;
procedure TearDown; override;
published
[TestCase('2;4')]
[TestCase('3;9')]
procedure TestDoStuff(Input, Output: Integer);
end;
implementation
procedure TMyClassTest.SetUp;
begin
inherited;
FSut := TMyClass.Create;
end;
procedure TMyClassTest.TearDown;
begin
inherited;
FSut.Free;
end;
procedure TMyClassTest.TestDoStuff(Input, Output: Integer);
begin
CheckEquals(Output, FSut.DoStuff(Input));
end;
initialization
RegisterTest(TMyClassTest.Suite);
end.运行它时,您的测试如下所示:

因为Delphi中的属性只接受常量,所以属性只将参数作为字符串,其中值由分号分隔。但是,没有什么可以阻止您创建自己的属性类,这些类接受正确类型的多个参数来防止“魔术”字符串。无论如何,您只限于可以是const的类型。
还可以在方法的每个参数上指定值属性,并使用任何可能的组合调用它(如NUnit中的那样)。
就其他答案而言,我想在编写单元测试时尽可能少地编写代码。另外,当我查看接口部分而不深入实现部分(我不会说:“让我们做BDD")时,我还想看看测试是做什么的。这就是为什么我更喜欢声明式的方式。
发布于 2012-01-25 13:33:29
我想你是在找这样的东西:
unit TestCases;
interface
uses
SysUtils, TestFramework, TestExtensions;
implementation
type
TArithmeticTest = class(TTestCase)
private
FOp1, FOp2, FSum: Integer;
constructor Create(const MethodName: string; Op1, Op2, Sum: Integer);
public
class function CreateTest(Op1, Op2, Sum: Integer): ITestSuite;
published
procedure TestAddition;
procedure TestSubtraction;
end;
{ TArithmeticTest }
class function TArithmeticTest.CreateTest(Op1, Op2, Sum: Integer): ITestSuite;
var
i: Integer;
Test: TArithmeticTest;
MethodEnumerator: TMethodEnumerator;
MethodName: string;
begin
Result := TTestSuite.Create(Format('%d + %d = %d', [Op1, Op2, Sum]));
MethodEnumerator := TMethodEnumerator.Create(Self);
Try
for i := 0 to MethodEnumerator.MethodCount-1 do begin
MethodName := MethodEnumerator.NameOfMethod[i];
Test := TArithmeticTest.Create(MethodName, Op1, Op2, Sum);
Result.addTest(Test as ITest);
end;
Finally
MethodEnumerator.Free;
End;
end;
constructor TArithmeticTest.Create(const MethodName: string; Op1, Op2, Sum: Integer);
begin
inherited Create(MethodName);
FOp1 := Op1;
FOp2 := Op2;
FSum := Sum;
end;
procedure TArithmeticTest.TestAddition;
begin
CheckEquals(FOp1+FOp2, FSum);
CheckEquals(FOp2+FOp1, FSum);
end;
procedure TArithmeticTest.TestSubtraction;
begin
CheckEquals(FSum-FOp1, FOp2);
CheckEquals(FSum-FOp2, FOp1);
end;
function UnitTests: ITestSuite;
begin
Result := TTestSuite.Create('Addition/subtraction tests');
Result.AddTest(TArithmeticTest.CreateTest(1, 2, 3));
Result.AddTest(TArithmeticTest.CreateTest(6, 9, 15));
Result.AddTest(TArithmeticTest.CreateTest(-3, 12, 9));
Result.AddTest(TArithmeticTest.CreateTest(4, -9, -5));
end;
initialization
RegisterTest('My Test cases', UnitTests);
end.它在GUI测试运行程序中如下所示:

我很想知道我是否以一种不太理想的方式来做这件事。DUnit是如此的通用和灵活,以至于每当我使用它时,我总是觉得我错过了一种更好、更简单的解决问题的方法。
发布于 2012-01-25 12:06:24
如果DUnit允许编写这样的代码,那么AddTestForDoStuff的每个调用都会创建一个类似于示例中的测试用例,这是否就足够了?
Suite.AddTestForDoStuff.With(2).Expect(4);
Suite.AddTestForDoStuff.With(3).Expect(9);我会在今天晚些时候发布一个例子.
对于.Net,已经有类似的东西:流利的断言
http://www.codeproject.com/Articles/784791/Introduction-to-Unit-Testing-with-MS-tests-NUnit-a
https://stackoverflow.com/questions/8999945
复制相似问题