Search code examples
c#visual-studiomauixunit

How to unit test MAUI app segment that uses the MainThread class


I am using xUnit to unit test my MAUI app.

I am testing my ViewModel class. The issue that I have is that a segment of code uses:

if (MainThread.IsMainThread)
{
    DoSomething();
}
else
{
    MainThread.BeginInvokeOnMainThread(DoSomething);
}

And xUnit is not able to use this code.

How would you go around that, ideally without having to specify special conditional symbols just for unit tests to run ? Is there a way to set up xUnit that I have missed ?

Note: here is the exception raised when such code is reached by code inside a xUnit project:

Microsoft.Maui.ApplicationModel.NotImplementedInReferenceAssemblyException: 'This functionality is not implemented in the portable version of this assembly. You should reference the NuGet package from your main application project in order to reference the platform-specific implementation.'


Solution

  • Usually, when you have dependencies that you can't control, either coming from the framework itself or from a 3rd party, you're not supposed to test them at all. If what you're attempting to test the DoSomething() method, a simple solution would be to introduce an interface and a wrapper class for the MainThread:

    interface IMainThread
    {
        bool IsMainThread {get;}
        void BeginInvokeOnMainThread(Action action); // not sure about this signrature
    }
    
    class MainThreadWrapper : IMainThread
    {
        public bool IsMainThread 
            => MainThread.IsMainThread;
        public void BeginInvokeOnMainThread(Action action) 
            => MainThread.BeginInvokeOnMainThread(action);
    }
    

    Then you inject that implementation to your testable class in the application, so your code looks like this:

    if (_mainThread.IsMainThread)
    {
        DoSomething();
    }
    else
    {
        _mainThread.BeginInvokeOnMainThread(DoSomething);
    }
    

    (_mainThread being an instance implementing the IMainThread interface)
    and in your tests you inject a mock or a fake for that interface.