6

I am running Unit Tests for my Xamarin.Forms application, and the Unit Tests throw Xamarin.Essentials.NotImplementedInReferenceAssemblyException:

enter image description here

I have created a Unit Test project for the app (using NUnit 3.12.0) and have written the below code to test the functionality.

[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**
    }
}

Below is the code under test from the Xamarin.Forms app:

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

The above functionality works fine in the Xamarin.Forms app.

Note: I read that await/async can be used, however, I will have to make changes in the entire project. This is not a feasible solution for now.


Edit 1: I have created a sample Xamarin.Forms project with Unit Tests in it. The project is available here

Sahil Khanna
  • 4,262
  • 8
  • 47
  • 72
  • Please take a look at https://github.com/xamarin/Essentials/issues/520, unit test with Xamarin.Essentials may need some more efforts with interfaces due to lots of reasons, and there are recommendations for unit test with Xamarin.Essentials. – Lia Oct 22 '20 at 02:29

4 Answers4

2

You need to add the Xamarin.Essentials NuGet Package to your Unit Test Project.

I highly recommend using Dependency Injection + Xamarin.Essentials.Interfaces, because certain Xamarin.Essentials APIs aren't implemented on .NET Core and you'll need to create your own implementation.

For example, here are some of the Xamarin.Essentials APIs I implemented in .NET Core for Unit Testing my GitTrends app:

Brandon Minnick
  • 13,342
  • 15
  • 65
  • 123
  • I have added Xamarin.Essentials already. Debuggung the code, I noticed that the test crashes (without any logs) on the below code. `HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead);` – Sahil Khanna Nov 15 '20 at 05:05
  • Hi @SahilKhanna! That sounds like a different question that requires a different solution to the question asked above. Since we’ve solved the initial question, let’s mark this as answered to help devs who find this question in the future. To solve the new issue you’re now facing, let’s open a new Question on StackOverflow. Feel free to link the question here as a comment and I’ll take a look! – Brandon Minnick Nov 16 '20 at 00:30
  • I feel see the issue is the same. The code executes correctly in the Android and iOS apps, however, I get the error in the Unit Tests. I'm getting the same error until now (as described in the question). I've just tried going a little deeper while debugging. – Sahil Khanna Nov 16 '20 at 05:20
  • I have created a sample Xamarin.Forms project with Unit Tests in it. The project is available at ... https://drive.google.com/drive/folders/1Lg2bQd_qN8xOYmgbglDY0WYv-RWEiREC?usp=sharing – Sahil Khanna Nov 21 '20 at 05:56
1

While you have stated that the subject function cannot be made async at this time, the test however can be made async.

TaskCompletionSource can be used to wait for the callback to be invoked.

[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")
    }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • I tried the solution you have posted. Unfortunately, I get the same error as posted in the screenshot in the question. Could there be some configuration/package related issue? – Sahil Khanna Oct 20 '20 at 14:11
  • I have created a sample Xamarin.Forms project with Unit Tests in it. The project is available at ... https://drive.google.com/drive/folders/1Lg2bQd_qN8xOYmgbglDY0WYv-RWEiREC?usp=sharing – Sahil Khanna Nov 21 '20 at 05:56
1

The issue can be related to what exactly the call back is trying to do. For example the Connectivity function of Xamarin.Essentials won't work (and will throw that type of exception) if you are calling it from your unit test as it is not running on a mobile platform and thus doesn't have that feature implemented.

A solution to this, at least one I've done for that type of functionality, is to create an interface. So, to continue with the example above regarding the Connectivity feature we can do this:

  1. Make an interface

    public interface IConnectivityService {
    
       bool CanConnect()
    }
    
  2. Define both an actual implementation of it that will be used during the app running, and one for testing purposes

    public ConnectivityService : IConnectivityService {
    
       //implement the method
    
    }
    
    public ConnectivtyServiceMock : IConnectivityService {
    
       //implement the method, but this is a mock, so it can be very skinny
    
    }
    
  3. Update your View/VM to accept this interface, and inside of the test case instantiate the mock version of it and pass that in.

In such scenarios where we have to reference device specific implementations from a unit test, we have to abstract it away in an interface and use that implementation instead

SomeStudent
  • 2,856
  • 1
  • 22
  • 36
  • I'm not using Xamarin.Essentials.Connectivity. While debugging, the test crashes (without any logs) on the below code. `HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead);`. I execute this code in a Try/Catch block, however, it just crashes i.e. it doesn't go to the catch block – Sahil Khanna Nov 19 '20 at 13:29
  • The example of connectivity was just that, an example of situation I came across which was resolved by wrapping it in an interface and having the test classes call a mock variant of it. Now, in your code, does it die after trying to send the message? I don't see how your test project won't have a reference of what HttpClient is given that it is part of System.Net – SomeStudent Nov 19 '20 at 19:32
  • I have created a sample Xamarin.Forms project with Unit Tests in it. The project is available at ... https://drive.google.com/drive/folders/1Lg2bQd_qN8xOYmgbglDY0WYv-RWEiREC?usp=sharing – Sahil Khanna Nov 20 '20 at 08:25
  • @SahilKhanna can you please post the app as a github url? I don't think any of us want to go to a google drive to download it – SomeStudent Dec 07 '20 at 20:04
1

I checked your code and it is not throwing any Xamarin.Essentials exception. To run the unit test, I had to add the "NUnit3TestAdapter" nuget package.

You will have to wait for callback otherwise test will crash. You can use TaskCompletionSource suggested by @Nkosi.

[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);
        }
Jai Gupta
  • 1,374
  • 9
  • 8