Creating and integration testing a .NET / C# Backend for Azure Mobile Services

I’ve been experimenting with the new .NET backend for Azure Mobile Services, and for various reasons decided I’d get the highest business value from testing the backend API controllers at a high level rather than unit testing them. This proved to be a little tricky as there isn’t a lot of documentation out there on the .NET backend yet, so I’ll cover the steps I took to get it working with a quick walkthrough.

Creating the backend

Firstly, let’s create a new mobile service and grab the sample app generated by the portal.

Open up the sample app in Visual Studio. You should give it a test run at this point to make sure it all works correctly.

Testing an API method

All looking good? Awesome. Add a .NET 4.5+ class library for your tests and set up the unit testing framework of your choice. You’ll also need to install the WindowsAzure.MobileServices.Backend.Entity NuGet package into your tests project.

While you’re there, add a connection string to the app.config in your tests project by copying in the one from web.config and changing the file and initial catalog names. This will be used for our test database.

Let’s see what happens when we add a really simple test:

Cool, wouldn’t have been any fun if it was that easy. If you take a look at the TodoItemController you’ll notice some dependencies configured in the Initialize method, which isn’t called by our test. We’ll match that with a fake implementation in our test setup method - we can add further information to the Request and Services objects if necessary for more complex tests. You’ll notice I’ve also chosen to manually configure the DataDirectory used in the test connection string (something that ASP.NET does automatically for our normal requests) so that the test database is created correctly under bin\Debug.

With that in place, let’s give our basic test another run.

Troubleshooting

I'm certainly no expert on the .NET Mobile Services backend, so I'm sure I've missed things in the above steps for more complex scenarios. As I explore more, I'll update the walkthrough and add potential issues below for advanced readers.

1. If you use |DataDirectory| in your test connection string but don't set this up, you'll see an error like this:

System.Data.SqlClient.SqlException
A file activation error occurred. The physical file name '\{databasename}.mdf' may be incorrect. Diagnose and correct additional errors, and retry the operation.
CREATE DATABASE failed. Some file names listed could not be created. Check related errors.

2. If whatever reason your version doesn't include these two calls, you might face errors like these:

System.Data.SqlClient.SqlException: Cannot create more than one clustered index on table 'dbo.TodoItems'. Drop the existing clustered index 'PK_dbo.TodoItems' before creating another.

Validation failed for one or more entities. See 'EntityValidationErrors' property for more details. errors complaining about your Id or CreatedAt properties being NULL, or a Cannot insert the value NULL into column error (especially if your seed data expects the database IDs to be auto generated).

There are two things going on here which cause this - bear with me as it gets a bit crazy! The Microsoft.WindowsAzure.Mobile.Service.ServiceConfig.Initialize() method called from our scaffolded WebApiConfig.Register() method injects it's own SQL rewriting method into EntityFramework using the Microsoft.WindowsAzure.Mobile.Service.Config.EntityExtensionConfig and Microsoft.WindowsAzure.Mobile.Service.Tables.EntityTableSqlGenerator classes included with the backend libraries. This SQL generator performs some functionality behind the scenes which prevent the above errors occuring, like configuring autogenerated Guids and DateTimes for the EntityData properties marked with TableColumnType.Id and TableColumnType.CreatedAt attributes, and force disabling index clustering on the primary key if you have an entity property marked with TableColumnType.CreatedAt (all defaults if your data models inherit from EntityData).

If your test doesn't call into a method from Microsoft.WindowsAzure.Mobile.Service.Entity or Microsoft.WindowsAzure.Mobile.Service.Entity.Tables assemblies which both contain [assembly: ExtensionConfigProvider(typeof (EntityExtensionConfig))] in their AssemblyInfo.cs files, you might not load those assemblies into your test project app domain - my initial approach to this involved the controller handling the initialisation in a different way, which meant the test never touched those assemblies.

Unfortunately, the SQL generator is injected by scanning your loaded assemblies for the attribute, which is never an issue for your main API project - see internal static void InitializeExtensions(_Assembly[] loadedAssemblies, HttpConfiguration config, ContainerBuilder containerBuilder) within Microsoft.WindowsAzure.Mobile.Service.ConfigBuilder for how this has been implemented. If you're having this problem, try adding the above attribute to your test AssemblyInfo.cs file yourself.

Wrapping it up

I recommend that if using migrations, when running in a test you should configure your seed class to to fully drop and recreate the database each time otherwise it's easy to miss configuration changes that will break the backend down the track.

I've created a base class for my controllers and for my controller tests to add support for this as well as simplify our controllers and tests:

Usage looks like this:

Tweet