問題: 単体テストの実行時に、Application.Current がクラス内で null になります。
解決方法: テスト クラスを実行するアプリケーション ドメインを作成します。アプリケーション ドメインは、テスト クラスのディスパッチャを提供します。
コード例:
MyClassInTheAssembly myClass;
string path = @"C:\\pathToTheDll\\MyAssembly.dll";
path = path.Replace(@"\\", @"\");
AppDomainSetup setup = new AppDomainSetup { PrivateBinPath = path };
AppDomain domain = AppDomain.CreateDomain("MyDomain", null, setup);
myClass = (MyClassInTheAssembly)domain.CreateInstanceFromAndUnwrap(path, typeof(MyClassInTheAssembly).FullName);
myClass オブジェクトは、Application.Current.Dispatcher を使用するパブリック メソッドの単体テストに使用できます。
myClass で単体テストを実行すると、次のエラーが表示されます。
タイプ 'MyAssembly.MyClassInTheAssembly' のコンストラクターが見つかりません。
このエラーは、domain.CreateInstanceFromAndUnwrap() メソッドを呼び出してテスト クラスのインスタンスを作成しているときに生成されます。
MyClassInTheAssembly コンストラクターには 2 つの必須パラメーターがあります。パラメータを使用してクラスの AppDomain インスタンスを作成することは可能ですか?
更新 私を正しい方向に向けてくれたuser779967に感謝します。上記の私の質問に対する答えは、AppDomain.CreateInstanceFromAndUnwrap() のメソッド オーバーライドを使用することです。メソッドのオーバーライドを使用して、AppDomain を使用してテスト クラスのインスタンスを作成しようとしました。
object[] parameters = new object[2]; // object array to pass my required parameters.
parameters[0] = container; // Required constructor param for myClass.
parameters[1] = eventAggregator; // Required constructor param for myClass.
myClass = (MyClassInTheAssembly)domain.CreateInstanceFromAndUnwrap(path, typeof(MyClassInTheAssembly).FullName, true, BindingFlags.Default, null, parameters, null, null);
アイデアは、このメソッド オーバーライドを使用して、必要なパラメーターをテスト クラスのインスタンス化に渡すことでした。次に、すべての単体テストにディスパッチャー スレッドを提供する AppDomain から派生したテスト クラスを使用できます。
このコードを実行すると、次の SerializationException を受け取りました: Type is not resolve for member 'Castle.DynamicProxy.Serialization.ProxyObjectReference,NSubstitute, Version=1.6.1.0, Culture=neutral, PublicKeyToken=92dd2e9066daa5ca'. これは、コンテナーとイベントアグリゲーター クラスの単体テストに NSubstitute を使用する必要があるためです。AppDomain メソッドは、NSubstitute ではなく、コンストラクターのパラメーターの型に対して IUnityContainer と IEventAggregator の正確なパラメーターの型の一致を探しています。
AppDomain を使用して単体テスト用のテスト クラスをインスタンス化する試みを断念しました。次のように、テスト クラス メソッド内で Application.Current を処理することにしました。
var dispatcher = Application.Current != null ? Application.Current.Dispatcher : Dispatcher.CurrentDispatcher;
if (dispatcher.CheckAccess())
{
do something....
}
else dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => CallMethodAgain()));
私のコードを本番環境で実行する場合、Application.Current は決して null であってはなりません。単体テスト用にコードを実行するときは、Dispatcher.CurrentDispatcher を使用して、実行中のスレッドからディスパッチャーを作成します。
AppDomain を使用して単体テスト用のクラスをインスタンス化するための優れた実装を誰かが持っている場合は、ぜひご覧ください。