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

Xamarin.Essentials.NotImplementedInReferenceAssemblyException thrown when Unit Testing Xamarin.Forms app

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

我已经为应用程序创建了一个单元测试项目(使用 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 添加到您的单元测试项目。

我强烈建议使用依赖注入 + Xamarin.Essentials.Interfaces,因为某些 Xamarin.Essentials API 未在 .NET Core 上实现,您需要创建自己的实现。

例如,这里有一些 Xamarin.Essentials APIs I implemented in .NET Core for Unit Testing my 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);
        }