我可以将一个集合传递给 DUnitX 中的测试用例吗?
Can I pass a set to a test case in DUnitX?
我正在尝试在 运行 测试后检查对象的状态。该状态包含在一个集合中。是否可以使用 DUnitX 属性将预期状态传递给测试用例,以便我可以对所有不同的输入使用相同的测试?
我试图将集合作为常量或集合传递,但在我的测试例程中它总是作为空集合到达。
- 这完全可以使用属性吗?
- 你会如何测试集合是否相同?
示例代码:
type
TResult = (resOK,resWarn,resError);
TResultSet = set of TResult;
const
cErrWarn : TResultSet = [resWarn];
type
[TestFixture]
TMyTest = class(TBaseTest)
[Test]
[TestCase('Demo1','InputA,[resWarn]')] // <-- tried as a set
[TestCase('Demo2','InputB,cErrWarn')] // <-- tried as a constant
procedure Test(Input:string; ExpectedResult: TResultSet);
end;
procedure TMyTest.Test(Input:string; ExpectedResult: TResultSet);
begin
// ExpectedResult is always the empty set []
RunTests(MyObject(Input));
Assert.AreEqual(ExpectedResult, MyObject.ResultSet);
end;
我还尝试将预期结果定义为数组,但 DUnitX 甚至不再调用测试。可能只是 "too much"
procedure Test(Input:string; ExpectedResult: array of TResult);
到目前为止我能想到的最好方法是使用以下方法。取最多三个(在此处插入您最喜欢的整数...)预期状态的样本,并分别检查这些状态。这不是我真正希望的,但确实有效。
procedure Test(Input:string; R1,R2,R3: TResult);
非常感谢您的帮助。 :)
您正在使用 TestCaseAttribute
指定要传递给测试方法的参数。但是,它根本不支持将集合作为参数传递。您可以通过查看 DUnitX.Utils
单元中声明的常量 Conversions
来了解情况。它将任何转换映射到一个集合 ConvFail
.
因此,如果您想使用属性指定此数据,您将需要扩展测试框架。您可以从 CustomTestCaseSourceAttribute
派生自己的后代并覆盖 GetCaseInfoArray
来解码您的设置参数。作为一个粗略的例子,你可以使用这个:
type
MyTestCaseAttribute = class(CustomTestCaseSourceAttribute)
private
FCaseInfo: TestCaseInfoArray;
protected
function GetCaseInfoArray: TestCaseInfoArray; override;
public
constructor Create(const ACaseName: string; const AInput: string; const AExpectedResult: TResultSet);
end;
constructor MyTestCaseAttribute.Create(const ACaseName, AInput: string; const AExpectedResult: TResultSet);
begin
inherited Create;
SetLength(FCaseInfo, 1);
FCaseInfo[0].Name := ACaseName;
SetLength(FCaseInfo[0].Values, 2);
FCaseInfo[0].Values[0] := TValue.From<string>(AInput);
FCaseInfo[0].Values[1] := TValue.From<TResultSet>(AExpectedResult);
end;
function MyTestCaseAttribute.GetCaseInfoArray: TestCaseInfoArray;
begin
Result := FCaseInfo;
end;
然后您可以将以下属性添加到您的测试方法中:
[MyTestCase('Demo2', 'InputB', [resWarn])]
procedure Test(Input: string; ExpectedResult: TResultSet);
为了简单起见,我在这里避免使用 RTTI,但使用 RTTI 会给您带来更大的灵活性。您可以将参数作为字符串传递,并使用 RTTI 对其进行解码,就像 DUnitX 中的代码一样。这意味着您无需在每次要使用集合参数时都编写定制属性。
更好的方法是通过将 Conversions
映射扩展到覆盖集,在 DUnitX 中实现这一点,并将其作为补丁提交。我相信其他人会发现它很有用。
将此转换函数添加到 DUnitX.Utils
并将其放入 tkUString
到 tkSet
的转换矩阵(由于 StringToSet
和 TValue
的限制这仅适用于最多 32 个元素的集合,但对于最多 256 个元素的更大集合,需要更多代码):
function ConvStr2Set(const ASource: TValue; ATarget: PTypeInfo; out AResult: TValue): Boolean;
begin
TValue.Make(StringToSet(ATarget, ASource.AsString), ATarget, AResult);
Result := True;
end;
此外,您还需要为参数使用不同的分隔符字符,否则会错误地拆分它们:
[TestCase('Demo1','InputA;[resWarn,resError]', ';')]
我正在尝试在 运行 测试后检查对象的状态。该状态包含在一个集合中。是否可以使用 DUnitX 属性将预期状态传递给测试用例,以便我可以对所有不同的输入使用相同的测试?
我试图将集合作为常量或集合传递,但在我的测试例程中它总是作为空集合到达。
- 这完全可以使用属性吗?
- 你会如何测试集合是否相同?
示例代码:
type
TResult = (resOK,resWarn,resError);
TResultSet = set of TResult;
const
cErrWarn : TResultSet = [resWarn];
type
[TestFixture]
TMyTest = class(TBaseTest)
[Test]
[TestCase('Demo1','InputA,[resWarn]')] // <-- tried as a set
[TestCase('Demo2','InputB,cErrWarn')] // <-- tried as a constant
procedure Test(Input:string; ExpectedResult: TResultSet);
end;
procedure TMyTest.Test(Input:string; ExpectedResult: TResultSet);
begin
// ExpectedResult is always the empty set []
RunTests(MyObject(Input));
Assert.AreEqual(ExpectedResult, MyObject.ResultSet);
end;
我还尝试将预期结果定义为数组,但 DUnitX 甚至不再调用测试。可能只是 "too much"
procedure Test(Input:string; ExpectedResult: array of TResult);
到目前为止我能想到的最好方法是使用以下方法。取最多三个(在此处插入您最喜欢的整数...)预期状态的样本,并分别检查这些状态。这不是我真正希望的,但确实有效。
procedure Test(Input:string; R1,R2,R3: TResult);
非常感谢您的帮助。 :)
您正在使用 TestCaseAttribute
指定要传递给测试方法的参数。但是,它根本不支持将集合作为参数传递。您可以通过查看 DUnitX.Utils
单元中声明的常量 Conversions
来了解情况。它将任何转换映射到一个集合 ConvFail
.
因此,如果您想使用属性指定此数据,您将需要扩展测试框架。您可以从 CustomTestCaseSourceAttribute
派生自己的后代并覆盖 GetCaseInfoArray
来解码您的设置参数。作为一个粗略的例子,你可以使用这个:
type
MyTestCaseAttribute = class(CustomTestCaseSourceAttribute)
private
FCaseInfo: TestCaseInfoArray;
protected
function GetCaseInfoArray: TestCaseInfoArray; override;
public
constructor Create(const ACaseName: string; const AInput: string; const AExpectedResult: TResultSet);
end;
constructor MyTestCaseAttribute.Create(const ACaseName, AInput: string; const AExpectedResult: TResultSet);
begin
inherited Create;
SetLength(FCaseInfo, 1);
FCaseInfo[0].Name := ACaseName;
SetLength(FCaseInfo[0].Values, 2);
FCaseInfo[0].Values[0] := TValue.From<string>(AInput);
FCaseInfo[0].Values[1] := TValue.From<TResultSet>(AExpectedResult);
end;
function MyTestCaseAttribute.GetCaseInfoArray: TestCaseInfoArray;
begin
Result := FCaseInfo;
end;
然后您可以将以下属性添加到您的测试方法中:
[MyTestCase('Demo2', 'InputB', [resWarn])]
procedure Test(Input: string; ExpectedResult: TResultSet);
为了简单起见,我在这里避免使用 RTTI,但使用 RTTI 会给您带来更大的灵活性。您可以将参数作为字符串传递,并使用 RTTI 对其进行解码,就像 DUnitX 中的代码一样。这意味着您无需在每次要使用集合参数时都编写定制属性。
更好的方法是通过将 Conversions
映射扩展到覆盖集,在 DUnitX 中实现这一点,并将其作为补丁提交。我相信其他人会发现它很有用。
将此转换函数添加到 DUnitX.Utils
并将其放入 tkUString
到 tkSet
的转换矩阵(由于 StringToSet
和 TValue
的限制这仅适用于最多 32 个元素的集合,但对于最多 256 个元素的更大集合,需要更多代码):
function ConvStr2Set(const ASource: TValue; ATarget: PTypeInfo; out AResult: TValue): Boolean;
begin
TValue.Make(StringToSet(ATarget, ASource.AsString), ATarget, AResult);
Result := True;
end;
此外,您还需要为参数使用不同的分隔符字符,否则会错误地拆分它们:
[TestCase('Demo1','InputA;[resWarn,resError]', ';')]