单元测试Xamarin.Forms应用程序时抛出Xamarin.Essentials.NotImplementedInReferenceAssemblyException

问题描述

我正在为我的Xamarin.Forms应用程序运行单元测试,并且单元测试抛出Xamarin.Essentials.NotImplementedInReferenceAssemblyException

enter image description here

我已经使用NUnit 3.12.0为该应用程序创建了一个单元测试项目,并编写了以下代码来测试该功能

[TestFixture()]
public class Test
{
    [test()]
    public void TestCase()
    {
        AutoResetEvent autoEvent = new AutoResetEvent(true);

        SomeClass someClass = new SomeClass();
        someClass.someFunction((response) =>
        {
            Assert.AreEqual(response,"Hello")
            autoEvent.Set();
        });

        autoEvent.WaitOne(); //** Xamarin.Essentials.NotImplementedInReferenceAssemblyException thrown here**
    }
}

以下是Xamarin.Forms应用中正在测试的代码

public class SomeClass
{
    
    public void SomeFunction(Action<string> callback)
    {
        // asynchronous code...
        callback("Hello");
    }
}

上述功能在Xamarin.Forms应用程序中正常运行。

注意:我读到可以使用await / async,但是,我将不得不在整个项目中进行更改。目前,这不是可行的解决方案。


修改1: 我创建了一个示例Xamarin.Forms项目,其中包含单元测试。该项目可用here

解决方法

虽然您已经声明此时无法使此主题函数异步,但是可以使测试异步。

TaskCompletionSource可用于等待回调被调用。

[TestFixture()]
public class Test {
    [Test()]
    public async Task TestCase() {
        //Arrange
        TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
        Action<string> callback = (arg) => {
            tcs.TrySetResult(arg);
        };
        SomeClass someClass = new SomeClass();

        //Act
        someClass.SomeFunction(callback);            
        string response = await tcs.Task;

        //Assert
        Assert.AreEqual(response,"Hello")
    }
}
,

您需要将Xamarin.Essentials NuGet Package添加到单元测试项目中。

我强烈建议您使用Dependency Injection + Xamarin.Essentials.Interfaces,因为某些Xamarin.Essentials API不在.NET Core上实现,因此您需要创建自己的实现。

例如,以下是一些Xamarin.Essentials APIs I implemented in .NET Core用于对我的GitTrends应用进行单元测试:

,

该问题可能与回叫正试图执行的操作有关。例如,如果您从单元测试中调用Xamarin.Essentials的连接功能,则该功能将无法工作(并抛出该类型的异常),因为该功能未在移动平台上运行,因此未实现该功能。

对此的一种解决方案是创建一个接口,这至少是我为该类型的功能所做的。因此,继续上面关于连接功能的示例,我们可以这样做:

  1. 建立界面

    public interface IConnectivityService {
    
       bool CanConnect()
    }
    
  2. 定义将在应用程序运行期间使用的实际实现以及用于测试的目的

    public ConnectivityService : IConnectivityService {
    
       //implement the method
    
    }
    
    public ConnectivtyServiceMock : IConnectivityService {
    
       //implement the method,but this is a mock,so it can be very skinny
    
    }
    
  3. 更新您的View / VM以接受此接口,然后在测试用例中实例化该接口的模拟版本并将其传入。

在这种情况下,我们必须从单元测试中引用特定于设备的实现,我们必须在接口中将其抽象出来,而改用该实现

,

我检查了您的代码,它没有引发任何Xamarin.Essentials异常。要运行单元测试,我必须添加“ NUnit3TestAdapter” nuget包。

您将必须等待回调,否则测试将崩溃。您可以使用@Nkosi建议的TaskCompletionSource。

[Test()]
        public async Task TestCase()
        {

            XamarinMock.Init();

            WebApi webApi = new WebApi();

            TaskCompletionSource<HttpResponseMessage> tcs = new TaskCompletionSource<HttpResponseMessage>();
            Action<HttpResponseMessage> callback = (arg) => {
                tcs.TrySetResult(arg);
            };

            webApi.Execute(callback);

            var response = await tcs.Task;
            Console.WriteLine(response);
            Assert.AreEqual(response.StatusCode,HttpStatusCode.OK);
        }