尝试从资源字典访问图像资源时,多个NUnit测试类失败

时间:2013-01-15 07:03:57

标签: c# wpf unit-testing nunit

我有一个用C#(.NET 4.0)编写的复杂WPF项目,我为(NUnit)编写了几个测试。这些测试存在于不同的类中,只要我为每个类单独运行测试,一切都很好。但是,一旦我尝试同时运行所有类的所有测试,第一个类的测试成功,但是一旦testrunner(Resharper或nunit-console)开始测试剩余的类,所有这些类都会失败,并显示以下堆栈跟踪。 / p>

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或testrunners使用单个线程,在每个测试类运行或类似之后完全退出?

编辑1:到目前为止我尝试了什么:

  • 使用[RequiresSTA]属性
  • 装饰测试
  • 在运行每个测试类之前,重新设置ResourceDictionaryManager类(发生此错误的位置)。这解决了ResourceDictionaryManager类中的问题,但是在代码中“稍后”出现了完全相同的问题。
  • 将所有测试复制到同一个巨大的类中。所有测试都运行良好(但这不是我想要的)

问题似乎是NUnit为每个包含测试方法的类使用不同的线程,所以我要么找到一种方法

  • 告诉NUnit在同一个线程中运行所有测试类

OR

  • 告诉NUnit在TestFixtureTearDown方法中完全关闭应用程序,以便我可以使用new Application();
  • 在下一个测试类中实例化一个新应用程序

4 个答案:

答案 0 :(得分:7)

从堆栈跟踪中,您发布了一个“线程关联”问题 - 即您正在尝试更新与其创建的线程不同的线程上的UI元素。 BitmapDecoder派生自DispatcherObject,即它需要在单个线程上运行。在您的测试运行中,它似乎是在一个线程上创建的,然后方法调用(ToString)是从不同的线程创建的。

  • 您是否肯定您没有在代码中生成线程? NUnit AFAIK使用相同的线程在测试运行中运行所有测试。你在运行什么版本?
  • 您是否跨测试共享同一个MUSTANG.ShowCase.ResourceLibrary实例?如何为每个测试创建一个新实例,即隔离测试?

更新:我想我现在已经确定了它。

  • 如果测试夹具仅使用TestFixture属性标记(无线程要求),则所有测试都在单个MTA线程上运行
  • 如果您使用RequiresSTA属性标记每个TestFixture,我会看到跑步者将为每个测试夹具创建一个新的STA线程(这似乎与您报告的内容相匹配)。
  • 由于您希望在同一个STA线程上进行所有测试(跨越灯具),因此应在汇编级别(在AssemblyInfo.cs文件中)指定此项。您可以在每个夹具级别删除该属性。

[assembly: RequiresThread(ApartmentState.STA)]

答案 1 :(得分:1)

好的,我现在通过NUnit控制台运行带有/runlist参数的测试(例如C:\NUnit-2.6.0.12051\bin\nunit-console-x86.exe My.Assembly.dll /xml=result.xml /runlist=..\testlist.txt)来解决它,其中testlist.txt包含我所有测试类的完全限定名称。这样NUnit似乎完全重启了每个要测试的类的应用程序。这个方法的缺点是,我必须添加到这个列表,一旦我添加一个新的测试类,但是现在,这个解决方案对我来说很好。

答案 2 :(得分:0)

尝试将RequiresSTA属性添加到测试夹具中。见这里:http://www.nunit.org/index.php?p=requiresSTA&r=2.5.9

还有RequiresThreadRequiresMTA(多线程)属性,链接自同一篇文章。它可能无法解决您的问题,但听起来应该是这样。

答案 3 :(得分:0)

一种选择是确保FreezeImageSourceResourceDictionary个对象。如果Freezable(例如ImageSource)被冻结,则可以从多个线程访问它,但是不可修改。