4

C#(.NET 4.0)で記述された複雑なWPFプロジェクトがあり、(NUnit)用にいくつかのテストを記述しました。これらのテストはさまざまなクラスにあり、各クラスのテストを個別に実行する限り、すべて問題ありません。ただし、すべてのクラスのすべてのテストを一度に実行しようとすると、最初のクラスのテストは成功しますが、テストランナー(Resharperまたはnunit-console)が残りのクラスのテストを開始すると、次のスタックトレースですべてが失敗します。

System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it
at System.Windows.Threading.Dispatcher.VerifyAccess()
at System.Windows.Media.Imaging.BitmapDecoder.ToString()
at System.Windows.Media.Imaging.BitmapFrameDecode.ConvertToString(String format, IFormatProvider provider)
at System.Windows.Media.ImageSource.ToString()
at MUSTANG.ShowCase.ResourceLibrary.ResourceDictionaryManager.GetUriString(String pKey) in c:\Daten\Jenkins-ci\jobs\MUSTANG-Showcase-Release-VS2010\workspace\MUSTANG-Showcase\MUSTANG.ShowCase.ResourceLibrary\ResourceDictionaryManager.cs:Zeile 49.

対応するコードは次のとおりです。

public object GetValue(string pKey)
{
    if (mDictionary.Contains(pKey))
    {
        return mDictionary[pKey];
    }
    return null;
}

public String GetUriString(string pKey)
{
    object result = GetValue(pKey);
    if (null == result)
    {
        Log.Warn(string.Format(@"Ressource '{0}' nicht gefunden!", pKey));
        return "";
    }
    return result.ToString();
}

リソースが画像の場合、GetUriStringの最後の行で例外が発生します。Nunitは、さまざまなスレッドを使用してさまざまなテストクラスを実行しているようですが、それでも順番に実行されます。この問題を解決する方法はありますか?たとえば、NUnitまたはテストランナーに単一のスレッドを使用するように指示したり、各テストクラスの実行後に完全に終了したりするなどの方法はありますか?

編集1:私がこれまでに試したこと:

  • [RequiresSTA]属性でテストを飾る
  • 各テストクラスを実行する前に、ResourceDictionaryManagerクラスをリセットします(このエラーが発生する場所です)。これにより、ResourceDictionaryManagerクラスの問題は解決しましたが、コードの「後で」まったく同じ問題が発生します。
  • すべてのテストを同じ巨大なクラスにコピーします。すべてのテストは正常に実行されます(ただし、それは私が望むものではありません)

問題は、NUnitがテストメソッドを含むクラスごとに異なるスレッドを使用していることであるように思われるので、次の方法を見つける必要があります。

  • 同じスレッドですべてのテストクラスを実行するようにNUnitに指示します

また

  • TestFixtureTearDownメソッドのNUnitに、アプリケーションを完全にシャットダウンするように指示します。これにより、次のテストクラスで新しいアプリケーションをインスタンス化できます。new Application();
4

4 に答える 4

8

スタックトレースから、「スレッドアフィニティ」の問題があるように見えます。つまり、作成されたスレッドとは異なるスレッドでUI要素を更新しようとしています。BitmapDecoderはDispatcherObjectから派生しています。つまり、単一のスレッドで動作する必要があります。テスト実行では、1つのスレッドで作成されてから、別のスレッドからメソッド呼び出し(ToString)が作成されているようです。

  • コードにスレッドを生成していないことを確信していますか?NUnit AFAIKは、同じスレッドを使用して、テスト実行内のすべてのテストを実行します。どのバージョンを実行していますか?
  • MUSTANG.ShowCase.ResourceLibraryテスト間で同じインスタンスを共有していますか?テストごとに新しいインスタンスを作成する、つまりテストを分離するのはどうですか?

更新:私は今それを釘付けにしていると思います。

  • テストフィクスチャがTestFixture属性だけでマークされている場合(スレッド要件なし)、すべてのテストは単一のMTAスレッドで実行されます
  • 各TestFixtureにRequiresSTA属性をマークすると、ランナーが各テストフィクスチャに対して新しいSTAスレッドを作成することがわかります(これは、報告しているものと一致しているようです)。
  • すべてのテスト(フィクスチャ全体)を同じSTAスレッドで実行する必要があるため、これをアセンブリレベルで(AssemblyInfo.csファイルで)指定する必要があります。すべてのフィクスチャレベルで属性を削除できます。

[assembly: RequiresThread(ApartmentState.STA)]
于 2013-01-15T07:12:04.480 に答える
1

OK、私は今、すべてのテストクラスの完全修飾名を含む/runlistパラメータ(例C:\NUnit-2.6.0.12051\bin\nunit-console-x86.exe My.Assembly.dll /xml=result.xml /runlist=..\testlist.txt)を使用してNUnitコンソールを介してテストを実行することで問題を解決しました。testlist.txtこのようにして、NUnitは、テストするクラスごとにアプリケーション全体を完全に再起動するように見えます。このメソッドの欠点は、新しいテストクラスを追加したら、このリストに追加する必要があることですが、今のところ、このソリューションは問題ありません。

于 2013-01-15T15:59:59.090 に答える
0

RequiresSTAテストフィクスチャに属性を追加してみてください。ここを参照してください:http ://www.nunit.org/index.php?p = requireSTA&r = 2.5.9

同じ記事からリンクされている(マルチスレッド)属性もRequiresThreadあります。RequiresMTA問題が解決しない場合もありますが、問題は解決するようです。

于 2013-01-15T07:20:42.460 に答える
0

1つのオプションは、内のFreezeすべてのImageSourceオブジェクトを確認することですResourceDictionaryFreezable(などのImageSource)がフリーズしている場合、複数のスレッドからアクセスできますが、変更することはできません。

于 2013-01-15T07:25:37.293 に答える