Mocking Xamarin.Forms

images

We’ve been building a large Xamarin.Forms application for quite some time at Hitcents, and there was a huge paint point I was able to figure out for our team. If you’ve ever gotten into Xamarin.Forms development, it becomes quite clear that there isn’t a guide for unit testing things like markup extensions, custom views, navigation, animations, etc.

If you have tried it yourself, you probably ran into something like this:FAIL

What Xamarin.Forms is asking for here is the code snippet that initializes Forms on each platform. So for example:

//On Android in MainActivity.cs
Xamarin.Forms.Forms.Init(this, bundle);

//On iOS in AppDelegate.cs
Xamarin.Forms.Forms.Init();

The dilemma is, if you try set setup your own Xamarin.Forms.Forms.Init() for the purposes of unit testing, you quickly find that most of the interfaces needed to set this up are marked internal. To get around this issue I took advantage of [InternalsVisibleTo], and created a unit testing library where I renamed the output assembly to Xamarin.Forms.Core.UnitTests.dll. This allowed me to setup a MockForms.Init() method, and this assembly name isn’t likely to conflict with anything in a developer’s Xamarin.Forms project.

So how do I set this up in my project?

Conveniently, I’ve open sourced this little library named Xamarin.Forms.Mockson Github, and I’ve even setup a package on NuGet for easy setup.

To get started, you simple have to call MockForms.Init() via a setup method of the unit testing framework of your choice. Here is an NUnit example:

[SetUp]
public void SetUp()
{
    Xamarin.Forms.Mocks.MockForms.Init();
}

This opens the door for all kinds of interesting tests:

//Dynamically load XAML
// NOTE: At the top, include using Xamarin.Forms.Xaml;
[Test]
public void LoadFromXaml()
{
    var label = new Label();
    label.LoadFromXaml("<Label Text=\"Woot\" />");
    Assert.AreEqual("Woot", label.Text);
}

//Unit test navigation
[Test]
public async Task Push()
{
    var root = new ContentPage();
    var page = new ContentPage();
    await root.Navigation.PushAsync(page);
    Assert.AreEqual(root.Navigation.NavigationStack.Last(), page);
}

//Unit test animations
[Test]
public async Task FadeTo()
{
    var view = new BoxView();
    await view.FadeTo(0);
    Assert.AreEqual(0, view.Opacity);
}

//Unit test a markup extension
[Test]
public void MarkupExtension()
{
    var label = new Label();
    label.LoadFromXaml("<Label xmlns:f=\"clr-namespace:Xamarin.Forms.Mocks.Tests;assembly=Xamarin.Forms.Mocks.Tests\" Text=\"{f:Terrible}\" />");
    Assert.AreEqual("2016", label.Text); //amirite?
}

//Here is the markup extension
public class TerribleExtension : IMarkupExtension<string>
{
    public string ProvideValue(IServiceProvider serviceProvider)
    {
        return "2016";
    }

    object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
    {
        return ProvideValue(serviceProvider);
    }
}

Hopefully, this is helpful to Xamarin.Forms developers out there wanting to use more TDD. There are still a few pieces of Xamarin.Forms internals not implemented yet. Let me know if there is something missing you need and get involved.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>