私が書いたWCFホスト管理エンジンの単体テストを試みています。エンジンは基本的に、構成に基づいてその場でServiceHostインスタンスを作成します。これにより、新しいサービスが追加されたり古いサービスが削除されるたびに、すべてのサービスを停止して再起動することなく、利用可能なサービスを動的に再構成できます。
ただし、ServiceHostの動作方法が原因で、このホスト管理エンジンの単体テストが困難になりました。特定のエンドポイントに対してServiceHostが既に作成、開かれ、まだ閉じられていない場合、同じエンドポイントの別のServiceHostを作成できず、例外が発生します。最新の単体テストプラットフォームではテストの実行が並列化されるため、このコードを単体テストする効果的な方法はありません。
私はxUnit.NETを使用しましたが、その拡張性のために、強制的にテストをシリアルに実行する方法を見つけることができると期待しています。しかし、私は運がありませんでした。ここで誰かがSOで同様の問題に遭遇し、単体テストをシリアルに実行する方法を知っていることを望んでいます。
注: ServiceHost は、Microsoftによって作成されたWCFクラスです。私はそれの振る舞いを変える能力を持っていません。各サービスエンドポイントを1回だけホストすることも適切な動作です。ただし、単体テストには特に役立ちません。
上記のように、すべての適切な単体テストは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");
}
}
}
詳細については、 このリンク を参照してください。
.NET Coreプロジェクトの場合、xunit.runner.json
with:
{
"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プロジェクトの場合、 https://xunit.github.io/docs/configuring-with-json.html で文書化されているように、xunit.runner.json
ファイルでxUnitを構成できます。
並列テストの実行を停止するために変更する必要がある設定はparallelizeTestCollections
で、デフォルトはtrue
です。
アセンブリがこのアセンブリ内で互いに並行してテストを実行する場合、これを
true
に設定します。 ...これをfalse
に設定して、このテストアセンブリ内のすべての並列化を無効にします。JSONスキーマタイプ:boolean
デフォルト値: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
で並列化をオフにしている場合でも、並列に実行します。 MicrosoftのUIデザイナーは、誤ってボタンを押す可能性を最大化するために、このボタンをラベルなし、気づきにくく、テストエクスプローラーの"Run All"ボタンから約1センチ離しています。また、テストが突然失敗する理由がわかりません。
Playlistを使用できます
テストメソッドを右クリック->プレイリストに追加->新しいプレイリスト
実行順序を指定できます。デフォルトでは、プレイリストに追加しますが、プレイリストファイルは必要に応じて変更できます
詳細はわかりませんが、単体テストではなく統合テストをしようとしているようです。 ServiceHost
への依存関係を分離できれば、テストが簡単(かつ高速)になります。 (たとえば)次を個別にテストできます。
IServiceHostFactory
とIConfiguration
をとるエンジンクラス分離(モック)フレームワークおよび(オプションで)IoCコンテナーフレームワークを含めるのに役立つツール。見る:
Advanced Unit Testing を使用できます。 テストを実行する順序を定義できます 。そのため、これらのテストをホストするために新しい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.");
...
}
動作するかどうか教えてください。