我正在尝试对我编写的WCF主机管理引擎进行单元测试。该引擎基本上基于配置动态创建ServiceHost实例。这允许我们动态地重新配置哪些服务可用,而无需在添加新服务或删除旧服务时将其全部关闭并重新启动它们。
但是,由于ServiceHost的工作方式,我在单元测试此主机管理引擎时遇到了困难。如果已为特定端点创建,打开和尚未关闭ServiceHost,则无法创建同一端点的另一个ServiceHost,从而导致异常。由于现代单元测试平台并行化了他们的测试执行,我没有有效的方法对这段代码进行单元测试。
我使用过xUnit.NET,希望由于它的可扩展性,我可以找到一种方法来强制它以串行方式运行测试。但是,我没有运气。我希望SO上的某个人遇到类似的问题并且知道如何让单元测试连续运行。
注意:ServiceHost是一个由Microsoft编写的WCF类。我没有能力改变它的行为。仅托管每个服务端点也是正确的行为......但是,它不是特别有利于单元测试。
如上所述,所有良好的单元测试应该是100%隔离的。使用共享状态(例如,取决于每个测试修改的static
属性)被认为是不好的做法。
话虽如此,关于按顺序运行xUnit测试的问题确实有答案!我遇到了完全相同的问题,因为我的系统使用静态服务定位器(不太理想)。
默认情况下,xUnit 2.x并行运行所有测试。通过在测试项目的AssemblyInfo.cs中定义CollectionBehavior
,可以对每个程序集进行修改。
对于每个组件分离使用:
using Xunit;
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
或者根本没有并行化:
[assembly: CollectionBehavior(DisableTestParallelization = true)]
后者可能就是你想要的那个。有关并行化和配置的更多信息可以在xUnit documentation上找到。
每个测试类都是一个独特的测试集合,其下的测试将按顺序运行,因此如果您将所有测试放在同一个集合中,那么它将按顺序运行。
在xUnit中,您可以进行以下更改以实现此目的:
以下将并行运行:
namespace IntegrationTests
{
public class Class1
{
[Fact]
public void Test1()
{
Console.WriteLine("Test1 called");
}
[Fact]
public void Test2()
{
Console.WriteLine("Test2 called");
}
}
public class Class2
{
[Fact]
public void Test3()
{
Console.WriteLine("Test3 called");
}
[Fact]
public void Test4()
{
Console.WriteLine("Test4 called");
}
}
}
要使其顺序,您只需将两个测试类放在同一个集合下:
namespace IntegrationTests
{
[Collection("Sequential")]
public class Class1
{
[Fact]
public void Test1()
{
Console.WriteLine("Test1 called");
}
[Fact]
public void Test2()
{
Console.WriteLine("Test2 called");
}
}
[Collection("Sequential")]
public class Class2
{
[Fact]
public void Test3()
{
Console.WriteLine("Test3 called");
}
[Fact]
public void Test4()
{
Console.WriteLine("Test4 called");
}
}
}
有关更多信息,请参阅this link
对于.NET Core项目,使用以下命令创建xunit.runner.json
:
{
"parallelizeAssembly": false,
"parallelizeTestCollections": false
}
此外,你的csproj
应该包含
<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
对于旧的.Net Core项目,您的project.json
应该包含
"buildOptions": {
"copyToOutput": {
"include": [ "xunit.runner.json" ]
}
}
对于.NET Core项目,您可以使用xunit.runner.json
文件配置xUnit,如https://xunit.github.io/docs/configuring-with-json.html所述。
您需要更改以停止并行测试执行的设置是parallelizeTestCollections
,默认为true
:
如果程序集愿意在此程序集内并行运行测试,则将此设置为
true
。 ...将此设置为false
以禁用此测试程序集中的所有并行化。JSON模式类型:布尔值 默认值:
true
因此,为此目的的最小xunit.runner.json
看起来像
{
"parallelizeTestCollections": false
}
如文档中所述,请记住在您的构建中包含此文件,方法是:
<Content Include=".\xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
到你的.csproj
文件,或"buildOptions": {
"copyToOutput": {
"include": [ "xunit.runner.json" ]
}
}
到你的project.json
文件取决于您的项目类型。
最后,除了上述内容之外,如果您使用的是Visual Studio,请确保您没有意外地单击“并行运行测试”按钮,这将导致测试并行运行,即使您已关闭xunit.runner.json
中的并行化。微软的UI设计人员狡猾地将这个按钮取消标记,很难注意到,并且距离测试资源管理器中的“全部运行”按钮大约一厘米,只是为了最大限度地提高您误操作的机会,并且不知道为什么要进行测试突然失败了:
你可以使用播放列表
右键单击测试方法 - >添加到播放列表 - >新播放列表
然后你可以指定执行顺序,默认是,当你将它们添加到播放列表但你可以根据需要更改播放列表文件
我不知道细节,但听起来你可能正在尝试进行集成测试而不是单元测试。如果您可以隔离对ServiceHost
的依赖性,那么这可能会使您的测试更容易(也更快)。所以(例如)你可以独立测试以下内容:
IServiceHostFactory
和IConfiguration
的引擎类有助于包括隔离(模拟)框架和(可选)IoC容器框架的工具。看到:
也许你可以使用Advanced Unit Testing。 It allows you to define the sequence in which you run the test。因此,您可能必须创建一个新的cs文件来托管这些测试。
以下是如何弯曲测试方法以按照您想要的顺序工作的方法。
[Test]
[Sequence(16)]
[Requires("POConstructor")]
[Requires("WorkOrderConstructor")]
public void ClosePO()
{
po.Close();
// one charge slip should be added to both work orders
Assertion.Assert(wo1.ChargeSlipCount==1,
"First work order: ChargeSlipCount not 1.");
Assertion.Assert(wo2.ChargeSlipCount==1,
"Second work order: ChargeSlipCount not 1.");
...
}
请告诉我它是否有效。
我在基类中添加了属性[Collection(“Sequential”)]:
namespace IntegrationTests
{
[Collection("Sequential")]
public class SequentialTest : IDisposable
...
public class TestClass1 : SequentialTest
{
...
}
public class TestClass2 : SequentialTest
{
...
}
}