如何使 xUnit 与 运行 理论并行?
How to make xUnit to run a Theory parallel?
我有一个很慢的测试(理论)和一堆测试用例。
所以我希望他们同时 运行。
我创建了一个简单的示例:
[Theory]
[MyTestData]
public void MyTheory(int num, int sleep)
{
Console.WriteLine("{0:HH:mm:ss.ffff} - Starting {1} - Sleeping {2}", DateTime.Now, num, sleep);
Thread.Sleep(sleep);
Console.WriteLine("{0:HH:mm:ss.ffff} - Finished {1} - Sleeping {2}", DateTime.Now, num, sleep);
}
[AttributeUsage(AttributeTargets.Method)]
public class MyTestDataAttribute : DataAttribute
{
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
yield return new object[2] { 1, 5000 };
yield return new object[2] { 2, 2000 };
yield return new object[2] { 3, 4000 };
}
}
运行测试的命令行:
"\packages\xunit.runner.console.2.0.0\tools\xunit.console" "\Projects\xUnitTest\xUnitTest\bin\Debug\xUnitTest.dll" -parallel all > xUnitResult.txt
结果:
xUnit.net console test runner(64-bit.NET 4.0.30319.42000)
Copyright(C) 2015 Outercurve Foundation.
Discovering: xUnitTest
Discovered: xUnitTest
Starting: xUnitTest
21:55:39.0449 - Starting 2 - Sleeping 2000
21:55:41.0627 - Finished 2 - Sleeping 2000
21:55:41.0783 - Starting 1 - Sleeping 5000
21:55:46.0892 - Finished 1 - Sleeping 5000
21:55:46.0892 - Starting 3 - Sleeping 4000
21:55:50.0989 - Finished 3 - Sleeping 4000
Finished: xUnitTest
=== TEST EXECUTION SUMMARY ===
xUnitTest Total: 3, Errors: 0, Failed: 0, Skipped: 0, Time: 11,137s
这很连续。
我确定可以使其并行。
从 xUnit 2.1 开始,这目前是不可能的。根据parallelization docs、
By default, each test class is a unique test collection. Tests within the same test class will not run in parallel against each other.
文档没有明确说明的是:
- xUnit 中最小的 "parallelizable" 单元是一个集合
- 无法将同一 class 中的测试放在不同的集合中
推而广之,不可能有并行化的理论,因为理论不能拆分成多个 classes。
对于您的情况,这意味着您至少有两个选择:
重构您的测试代码,以便抽象出任何占用大量时间的代码。例如,假设您必须 运行 一些业务逻辑,然后进行数据库调用。如果您可以将可测试的业务逻辑分离到另一个 class,您可以 运行 反对您的理论(不是并行,而是 <1 毫秒),并单独测试慢速数据访问代码。
如果您的测试用例足够少,只需为每个测试用例创建一个新的 class 并使用 Fact
而不是 Theory
。您甚至可以将它们全部放在一个文件中。它更冗长,你失去了使用理论的 "cool" 因素,但你会得到并行执行。
虽然不能直接使用 xUnit,但如果需要,您可以解决这个问题。有缺点,比如你必须通过 classes 手动定义并行执行的数量,所以如果你想在两个线程上并行化它,你需要创建两个 classes.
public abstract class ParellelTheoryBase
{
public static List<int> testValues = new List<int> {1, 2, 3, 4, 5, 6};
}
public class ParallelTheory_1of2 : ParellelTheoryBase
{
public static List<object[]> theoryData = testValues.Where((data, index) => index % 2 == 0).Select(data => new object[] { data }).ToList();
[Theory]
[MemberData(nameof(theoryData))]
public void DoSomeLongRunningAddition(int data)
{
Assert.True(data < 7);
Thread.Sleep(5000);
}
}
public class ParallelTheory_2of2 : ParellelTheoryBase
{
public static List<object[]> theoryData = testValues.Where((data, index) => index % 2 == 1).Select(data => new object[] { data }).ToList();
[Theory]
[MemberData(nameof(theoryData))]
public void DoSomeLongRunningAddition(int data)
{
Assert.True(data < 7);
Thread.Sleep(5000);
}
}
在这个例子中,我在ParellelTheoryBase
中定义了属性,它是实际测试classes的基础class。
然后,ParallelTheory_1of2
和 ParallelTheory_2of2
继承自 class 以访问 testValues
。现在,Theory
使用此 theoryData
进行测试执行,它仅从列表中选择奇数或偶数(index % 2 == 1
或 index % 2 == 0
)数据。
它在 Visual Studio 测试浏览器中被选中:
并并行运行:
[xUnit.net 00:00:00.3397963] Starting
[xUnit.net 00:00:15.5696617] Finished
这可能是一个解决方案,它不容易事先知道您有多少测试数据。例如,我在集成测试文件解析器时使用它,其中输入是目录中的任何文件,新添加的文件将自动被提取用于测试。
在您的示例中,我认为您可以轻松修改 MyTestData
属性以接受 totalCountParallels
和 currentIndex
的参数。
我有一个很慢的测试(理论)和一堆测试用例。 所以我希望他们同时 运行。
我创建了一个简单的示例:
[Theory]
[MyTestData]
public void MyTheory(int num, int sleep)
{
Console.WriteLine("{0:HH:mm:ss.ffff} - Starting {1} - Sleeping {2}", DateTime.Now, num, sleep);
Thread.Sleep(sleep);
Console.WriteLine("{0:HH:mm:ss.ffff} - Finished {1} - Sleeping {2}", DateTime.Now, num, sleep);
}
[AttributeUsage(AttributeTargets.Method)]
public class MyTestDataAttribute : DataAttribute
{
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
yield return new object[2] { 1, 5000 };
yield return new object[2] { 2, 2000 };
yield return new object[2] { 3, 4000 };
}
}
运行测试的命令行:
"\packages\xunit.runner.console.2.0.0\tools\xunit.console" "\Projects\xUnitTest\xUnitTest\bin\Debug\xUnitTest.dll" -parallel all > xUnitResult.txt
结果:
xUnit.net console test runner(64-bit.NET 4.0.30319.42000)
Copyright(C) 2015 Outercurve Foundation.
Discovering: xUnitTest
Discovered: xUnitTest
Starting: xUnitTest
21:55:39.0449 - Starting 2 - Sleeping 2000
21:55:41.0627 - Finished 2 - Sleeping 2000
21:55:41.0783 - Starting 1 - Sleeping 5000
21:55:46.0892 - Finished 1 - Sleeping 5000
21:55:46.0892 - Starting 3 - Sleeping 4000
21:55:50.0989 - Finished 3 - Sleeping 4000
Finished: xUnitTest
=== TEST EXECUTION SUMMARY ===
xUnitTest Total: 3, Errors: 0, Failed: 0, Skipped: 0, Time: 11,137s
这很连续。 我确定可以使其并行。
从 xUnit 2.1 开始,这目前是不可能的。根据parallelization docs、
By default, each test class is a unique test collection. Tests within the same test class will not run in parallel against each other.
文档没有明确说明的是:
- xUnit 中最小的 "parallelizable" 单元是一个集合
- 无法将同一 class 中的测试放在不同的集合中
推而广之,不可能有并行化的理论,因为理论不能拆分成多个 classes。
对于您的情况,这意味着您至少有两个选择:
重构您的测试代码,以便抽象出任何占用大量时间的代码。例如,假设您必须 运行 一些业务逻辑,然后进行数据库调用。如果您可以将可测试的业务逻辑分离到另一个 class,您可以 运行 反对您的理论(不是并行,而是 <1 毫秒),并单独测试慢速数据访问代码。
如果您的测试用例足够少,只需为每个测试用例创建一个新的 class 并使用
Fact
而不是Theory
。您甚至可以将它们全部放在一个文件中。它更冗长,你失去了使用理论的 "cool" 因素,但你会得到并行执行。
虽然不能直接使用 xUnit,但如果需要,您可以解决这个问题。有缺点,比如你必须通过 classes 手动定义并行执行的数量,所以如果你想在两个线程上并行化它,你需要创建两个 classes.
public abstract class ParellelTheoryBase
{
public static List<int> testValues = new List<int> {1, 2, 3, 4, 5, 6};
}
public class ParallelTheory_1of2 : ParellelTheoryBase
{
public static List<object[]> theoryData = testValues.Where((data, index) => index % 2 == 0).Select(data => new object[] { data }).ToList();
[Theory]
[MemberData(nameof(theoryData))]
public void DoSomeLongRunningAddition(int data)
{
Assert.True(data < 7);
Thread.Sleep(5000);
}
}
public class ParallelTheory_2of2 : ParellelTheoryBase
{
public static List<object[]> theoryData = testValues.Where((data, index) => index % 2 == 1).Select(data => new object[] { data }).ToList();
[Theory]
[MemberData(nameof(theoryData))]
public void DoSomeLongRunningAddition(int data)
{
Assert.True(data < 7);
Thread.Sleep(5000);
}
}
在这个例子中,我在ParellelTheoryBase
中定义了属性,它是实际测试classes的基础class。
然后,ParallelTheory_1of2
和 ParallelTheory_2of2
继承自 class 以访问 testValues
。现在,Theory
使用此 theoryData
进行测试执行,它仅从列表中选择奇数或偶数(index % 2 == 1
或 index % 2 == 0
)数据。
它在 Visual Studio 测试浏览器中被选中:
并并行运行:
[xUnit.net 00:00:00.3397963] Starting
[xUnit.net 00:00:15.5696617] Finished
这可能是一个解决方案,它不容易事先知道您有多少测试数据。例如,我在集成测试文件解析器时使用它,其中输入是目录中的任何文件,新添加的文件将自动被提取用于测试。
在您的示例中,我认为您可以轻松修改 MyTestData
属性以接受 totalCountParallels
和 currentIndex
的参数。