我有一些方法依赖于一些随机计算来提供建议,我需要多次运行这个Fact以确保它可行。
我可以在要测试的fact中包含一个for循环,但由于有几个测试我想这样做,所以我在寻找一种更干净的方法,类似于junit中的Repeat属性:http://www.codeaffine.com/2013/04/10/running-junit-tests-repeatedly-without-loops/
我能否在xunit中轻松实现类似的功能?
我有一些方法依赖于一些随机计算来提供建议,我需要多次运行这个Fact以确保它可行。
我可以在要测试的fact中包含一个for循环,但由于有几个测试我想这样做,所以我在寻找一种更干净的方法,类似于junit中的Repeat属性:http://www.codeaffine.com/2013/04/10/running-junit-tests-repeatedly-without-loops/
我能否在xunit中轻松实现类似的功能?
为了让xunit运行同一个测试多次,您需要创建一个新的DataAttribute
。
这里是一个遵循junit相同思路的示例:
public class RepeatAttribute : DataAttribute
{
private readonly int _count;
public RepeatAttribute(int count)
{
if (count < 1)
{
throw new ArgumentOutOfRangeException(nameof(count),
"Repeat count must be greater than 0.");
}
_count = count;
}
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
return Enumerable.Repeat(new object[0], _count);
}
}
有了这段代码,你只需要将Fact
更改为Theory
,并像这样使用Repeat
:
[Theory]
[Repeat(10)]
public void MyTest()
{
...
}
虽然我有同样的需求,但是被接受的答案代码并没有重复测试,所以我对其进行了修改:
public sealed class RepeatAttribute : Xunit.Sdk.DataAttribute
{
private readonly int count;
public RepeatAttribute(int count)
{
if (count < 1)
{
throw new System.ArgumentOutOfRangeException(
paramName: nameof(count),
message: "Repeat count must be greater than 0."
);
}
this.count = count;
}
public override System.Collections.Generic.IEnumerable<object[]> GetData(System.Reflection.MethodInfo testMethod)
{
foreach (var iterationNumber in Enumerable.Range(start: 1, count: this.count))
{
yield return new object[] { iterationNumber };
}
}
}
虽然在之前的示例中使用了Enumerable.Repeat,但它只会运行1次测试,某种方式xUnit没有重复测试。可能是他们一段时间以前改变了什么。
通过更改为foreach
循环,我们可以重复每个测试,但也提供了“迭代号”。
当在测试函数上使用它时,您必须向测试函数添加一个参数并将其装饰为Theory
,如下所示:
[Theory(DisplayName = "It should work")]
[Repeat(10)]
public void It_should_work(int iterationNumber)
{
...
}
这适用于 xUnit 2.4.0。
我创建了一个 NuGet 包,以便在有兴趣的情况下使用此功能。
GetData
可以简化为:return Enumerable.Range(1, this.count).Select(n => new object[] { n })
。 - oatsoda对于少量迭代的最简单的方法:将其作为理论而不是事实。 为每个迭代插入一行 [InlineData] 。
using Xunit;
namespace MyUnitTests
{
public class Class1
{
[Theory]
[InlineData]
[InlineData]
public void TestThis()
{
// test code here
}
}
}
已经使用 XUnit 2.4.1 进行测试。
`[SuppressMessage("Usage", "xUnit1025:InlineData should be unique within the Theory it belongs to")]
[SuppressMessage("Usage", "xUnit1006:Theory methods should have parameters")]`
- Ofiris我知道这个帖子有点旧了,但如果您需要重复进行少量测试,可以使用以下小技巧:
首先,创建一些虚拟数据:
public static IEnumerable<object[]> DummyTestData()
{
yield return new object[] { 0 };
yield return new object[] { 1 };
yield return new object[] { 2 };
yield return new object[] { 3 };
}
然后使用虚拟数据来强制测试对每个向量运行。在这种情况下,同一测试将被调用4次(但实际上未使用虚拟数据):
private static int _counter = 0;
[Theory]
[MemberData(nameof(DummyTestData))]
public void SomeTest(int dummyParam) // dummyParam is unused
{
_counter+= 1;
DoSomething();
Assert.True(...);
}
由于MemberData
还需要成员参数,因此您可以简单地传递迭代号以创建“DummyTestData”。
{{link1:}}
public static IEnumerable<object[]> MockParallel(int n)
{
foreach (var iterationNumber in Enumerable.Range(start: 1, count: n))
{
yield return new object[] { iterationNumber };
}
}
[Theory]
[MemberData(nameof(MockParallel), n)] // repeat n times
public void SomeTest(int repeat)
您可以使用ITestCaseOrderer
来对测试进行排序。在排序时,您可以多次指定某些测试。例如:
using System.Collections.Generic;
using System.Linq;
using Xunit.Abstractions;
using Xunit.Sdk;
namespace XUnit.Project.Orderers {
public class RepeatOrderer : ITestCaseOrderer {
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases) where TTestCase : ITestCase {
List<TTestCase> result = new();
testCases.ToList().ForEach(testCase => {
var repeat = (ReflectionAttributeInfo)testCase.TestMethod.Method.GetCustomAttributes(typeof(RepeatAttribute)).FirstOrDefault();
if (repeat != null && repeat.Attribute is RepeatAttribute) {
var attr = repeat.Attribute as RepeatAttribute;
Enumerable.Range(0, attr.Count).ToList().ForEach((_) => { result.Add(testCase); });
}
else {
result.Add(testCase);
}
});
return result;
}
}
}
属性
public class RepeatAttribute : Attribute {
public int Count { get; init; }
public RepeatAttribute(int count) {
this.Count = count;
}
}
指示测试项目使用排序器。在我的例子中,TestProject1
是包含排序器的程序集。
[assembly : TestCaseOrderer("XUnit.Project.Orderers.RepeatOrderer", "TestProject1")]
并使用Repeate
属性
[Fact]
[Repeat(6)]
public void Test1() {
...
}
GetData
中删除Type[] parameterTypes
参数。现在如果这可以与其他属性一起使用,比如CombinatorialData
就好了。 - gligoran