Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple NUnit Test Classes fail when trying to access an image resource from a Resource Dictionary

I have a complex WPF project written in C# (.NET 4.0) which I wrote a couple of tests for (NUnit). Theses tests reside in different classes and as long as I run the tests for each class individually everything is fine. However, once I try to run all tests of all classes at once the tests of the first class succeed, but once the testrunner (Resharper or nunit-console) starts testing the remaining classes, all of them fail with the following stack trace.

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.

The corresponding code is the following:

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();
}

The exception occurs on the last line in GetUriString when the resource was an image. Nunit seems to use different threads to run the different test classes - still they are run in sequence. Is there any way to resolve this problem e.g. by either telling NUnit or the testrunners to use a single thread, to completely quit after each test class run or similar?

Edit 1: What I have tried so far:

  • Decorating tests with [RequiresSTA] attribute
  • Before running each test class reseting the ResourceDictionaryManager class (which is where this error occurs). This solved the problem in the ResourceDictionaryManager class but then the exact same problem occurs "later" in code.
  • Copying all tests into the same gigantic class. All tests run fine (but that's not what I want)

The problem seems to be that NUnit uses a different thread for each class that contains test methods, so I either need to find a way to

  • tell NUnit to run all test classes in the same thread

OR

  • tell NUnit in the TestFixtureTearDown method to completely shutdown the application so I can instantiate a new application in the next test class using new Application();
like image 671
David Avatar asked Jan 14 '23 12:01

David


1 Answers

From the stacktrace, you posted it appears that you have a 'thread affinity' problem - i.e. you are trying to update a UI element on a thread different from the one that it was created on. BitmapDecoder is derived from DispatcherObject i.e. it needs to operate on a single thread. It seems in your test run, it is being created on one thread and then method calls (ToString) are being made from a different thread.

  • Are you positive that you are not spawning threads in your code ? NUnit AFAIK uses the same thread to run all tests within a test-run. What version are you running?
  • Are you sharing the same instance of MUSTANG.ShowCase.ResourceLibrary across tests? How about creating a new instance for each test i.e isolating tests ?

Update: I think I have it nailed down now.

  • If the test fixture is marked just with the TestFixture attribute, (No threading requirements), all tests are run on a single MTA thread
  • If you mark each TestFixture with the RequiresSTA attribute, I see that the runner will create a new STA thread for each test fixture (which seems to match what you're reporting).
  • Since you want all your tests (across fixtures) on the same STA thread, you should specify this at the assembly level (in the AssemblyInfo.cs file). You can drop the attribute at every fixture level.

.

[assembly: RequiresThread(ApartmentState.STA)]
like image 52
Gishu Avatar answered Jan 17 '23 14:01

Gishu