我对UnitTesting相当新,并拥有以下代码
public class PowerOf
{
public int CalcPowerOf(int @base, int exponent) {
if (@base == 0) { return 0; }
if (exponent == 0) { return 1; }
return @base * CalcPowerOf(@base, exponent - 1);
}
}
我首先为它编写的单元测试(使用xUnit)就是这个,但我不确定它是否是正确的方法,或者我是否应该使用其他模式? 我想知道的是指将多组数据传递到"单元测试" - 因为我没有在xUnit的文档上看到任何文档或参考示例?
[Fact]
public void PowerOfTest() {
foreach(var td in PowerOfTestData()) {
Assert.Equal(expected, CalcPowerOf(@base, exponent));
}
}
public class TestData {
int Base {get;set;}
int Exponent {get;set;}
int ExpectedResult {get;set;}
}
public List<TestData> PowerOfTestData() {
yield return new TestData { Base = 0, Exponent = 0, TestData = 0 };
yield return new TestData { Base = 0, Exponent = 1, TestData = 0 };
yield return new TestData { Base = 2, Exponent = 0, TestData = 1 };
yield return new TestData { Base = 2, Exponent = 1, TestData = 2 };
yield return new TestData { Base = 5, Exponent = 2, TestData = 25 };
}
答案 0 :(得分:6)
你最好在xUnit中使用一个名为Theory
的专用构造来处理所谓的“数据驱动测试”。
使用Theory
属性装饰您的测试方法,然后确保返回一个static
“成员”,其中包含输入参数和预期结果,就像您对TestData类所做的那样。请参阅下面的示例,并参考xUnit文档:"Writing your first theory"。
我会像下面那样重构你的代码。首先使用属性Theory
和MemberData
修饰测试,并在测试“@base”,“exponent”和“expectedResult”中添加参数 - 就像在TestData
课程中一样。 xUnit
将不允许您使用TestData类,它只接受IEnumerable<object>
并要求它是静态的,但foreach循环结构的好处是所有测试都是单独运行的。并且对于具有特定数据集的每次运行,您将获得绿色或红色标记!
public class PowerOfTests
{
[Theory]
[MemberData(nameof(PowerOfTestData))]
public void PowerOfTest(int @base, int exponent, int expected) {
Assert.Equal(expected, CalcPowerOf(@base, exponent));
}
public static IEnumerable<object[]> PowerOfTestData() {
yield return new object[] { 0, 0, 0 };
yield return new object[] { 0, 1, 0 };
yield return new object[] { 2, 0, 1 };
yield return new object[] { 2, 1, 2 };
yield return new object[] { 2, 2, 4 };
yield return new object[] { 5, 2, 25 };
yield return new object[] { 5, 3, 125 };
yield return new object[] { 5, 4, 625 };
}
}
答案 1 :(得分:3)
您正在使用类成员来定义您的数据,这在您的情况下是错误的。在运行时指定值时可以使用此方法(可能循环从1到MAX的值),这不是您的情况(您有硬编码数据)。我认为这种方法更好:
[Theory]
[InlineData(0, 0, 0)
[InlineData(0, 1, 0)
[InlineData(2, 0, 1)
[InlineData(2, 1, 2)
[InlineData(2, 2, 4)
[InlineData(5, 2, 25)
[InlineData(5, 3, 125)
[InlineData(5, 4, 625)
public void PowerOfTest(int @base, int exponent, int expected)
{
var result = CalcPowerOf(@base,exponent, exponent);
Assert.Equal(expected, result);
}
通过这种方式,您可以在大型课程中进行更具可读性的测试。
答案 2 :(得分:0)
对于测试方法的强类型参数列表而不使用 object[]
,您也可以使用 TheoryData
。它为最多 10 个参数定义了几个通用重载。由于您的方法有 3 个整数输入值,@base
、exponent
和 expected
,您可以使用 TheoryData<int, int, int>
类型的属性。然后,使用 PowerOfTest
和 Theory
属性注释您的 MemberData(nameof(PropertyName)
方法:
class PowerOfTests
{
TheoryData<int, int, int> PowerOfTestData => new TheoryData<int, int, int>
{
{ 0, 0, 0 },
{ 0, 1, 0 },
{ 2, 0, 1 },
{ 2, 1, 2 },
{ 5, 2, 25 }
};
[Theory]
[MemberData(nameof(PowerOfTestData)]
public void PowerOfTest(int @base, int exponent, int expected)
{
Assert.Equal(expected, CalcPowerOf(@base, exponent));
}
}
我可以使用以下内容初始化 TheoryData<int, int, int>
的原因:
{
{ param1, param2, param3 },
...
}
语法(称为collection initializer)是因为它实现了IEnumerable
并定义了一个Add<int, int, int>(int, int, int)
方法,该方法接受三个整数参数(<int, int, int>
的泛型重载{{1} }}).
这也使得通过继承 TheoryData
可以将测试数据放在一个单独的类中:
TheoryData
现在用 class PowerOfTestDataClass : TheoryData<int, int, int>
{
public PowerOfTestDataClass()
{
Add(0, 0, 0);
Add(0, 1, 0);
Add(2, 0, 1);
Add(2, 1, 2);
Add(5, 2, 25);
}
}
属性及其参数作为 MemberData
的类型注释 PowerOfTest()
方法,而不是 ClassData
:
PowerOfTestDataClass
具有强类型类型参数列表的优点是您始终可以确保参数具有正确的类型和正确的长度。虽然 [Theory]
[ClassData(typeof(PowerOfTestDataClass)]
public void PowerOfTest(int @base, int exponent, int expected)
{
Assert.Equal(expected, CalcPowerOf(@base, exponent));
}
中的对象数组也可以使用,但它允许任何类型和任何长度。
参考:https://andrewlock.net/creating-strongly-typed-xunit-theory-test-data-with-theorydata/