4

我有一个用 C# (.NET 4.0) 编写的复杂 WPF 项目,我为 (NUnit) 编写了几个测试。这些测试位于不同的类中,只要我为每个类单独运行测试,一切都很好。但是,一旦我尝试一次运行所有类的所有测试,第一个类的测试成功,但是一旦 testrunner(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 或 testrunners 使用单个线程,在每个测试类运行后完全退出或类似的?

编辑1:到目前为止我已经尝试过:

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

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

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

或者

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

4 回答 4

8

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

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

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

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

.

[assembly: RequiresThread(ApartmentState.STA)]
于 2013-01-15T07:12:04.480 回答
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 似乎为每个要测试的类完全重启了整个应用程序。这种方法的缺点是,我必须添加到这个列表中,一旦我添加了一个新的测试类,但现在,这个解决方案对我来说很好。

于 2013-01-15T15:59:59.090 回答
0

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

于 2013-01-15T07:25:37.293 回答
0

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

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

于 2013-01-15T07:20:42.460 回答