Testing ASP.NET Core MVC Controllers: Getting Started

When writing ASP.NET Core MVC web applications, you may want to test that controller actions behave in the expected way, for example that the action returns the correct result type (e.g. a ViewResult) or that the action behaves as expected when the model state is invalid.

To get started writing controller tests, first add a new .NET Core xUnit test project to the solution. This will create the test project along with requried xUnit.net NuGet packages. It will also add a default test class "UnitTest1.cs":

using System;
using Xunit;

namespace WebApplication1.Tests
{
    public class UnitTest1
    {
        [Fact]
        public void Test1()
        {
        }
    }
}

In the preceding code, notice the xUnit.net [Fact] attribute that marks the Test1 method as a test that should be executed by the test runner. One way to run tests in Visual Studio is to use the built-in Test Explorer which can be accessed via the menus: Test->Windows->Test Explorer.

If you build the project you will see the default test shown in the Test Explorer window:

Visual Studio Test Explorer

Adding a Controller Test

First, to get access to the controllers in the ASP.NET Core MVC application, add a reference to the web project from the test project. An instance of a controller can now be created in the test method:

var sut = new WebApplication1.Controllers.HomeController();

We can now call methods (actions) on the controller and verify the results. As a simple example, we can check that the Index method result is a view:

[Fact]
public void Test1()
{
    HomeController sut = new WebApplication1.Controllers.HomeController();

    IActionResult result = sut.Index();

    Assert.IsType<ViewResult>(result);
}

There are many different ways to test the results of controllers, including the ability to simulate model errors or using Moq mock objects as controller constructor dependencies.

The following code shows an excerpt from a controller and a test that examines the view's model that was returned:

public class PersonViewModel
{
    public string Name { get; set; }
}

public IActionResult Person()
{
    PersonViewModel viewModel = new PersonViewModel
    {
        Name = "Amrit"
    };

    return View(viewModel);
}
[Fact]
public void Test2()
{
    HomeController sut = new WebApplication1.Controllers.HomeController();

    IActionResult result = sut.Person();

    ViewResult viewResult = Assert.IsType<ViewResult>(result);

    PersonViewModel model = Assert.IsType<PersonViewModel>(viewResult.Model);

    Assert.Equal("Amrit", model.Name);
}

To learn how to get started testing ASP.NET Core MVC applications check out my ASP.NET Core MVC Testing Fundamentals Pluralsight course.

Mocking in .NET Core Tests with Moq

When writing automated tests it is sometimes useful to isolate the thing(s) being tested from other parts of the system. These ‘other’ parts may still need to be provided, and sometimes the real versions are too hard or cumbersome to use. In these instances “mocked” versions can be created and used.

A mock version of something is an object that can act like the real thing but can be controlled in test code.

Moq (pronounced “mok u” or “mock”) is a library available on NuGet that allows mock objects to be created in test code and it also supports .NET Core.

Moq allows the manipulation of mock objects in many ways, including setting mock methods to return specific values, setting up properties, and matching specific arguments when the thing being tested calls the mock object.

For example, the following code shows a class that requires a constructor dependency to be able to operate:

using System;

namespace Domain
{
    public interface IThingDependency
    {
        string JoinUpper(string a, string b);
        int Meaning { get; }
    }

    // "Real" implementation
    public class ThingDependency : IThingDependency
    {
        public string JoinUpper(string a, string b)
        {
            throw new NotImplementedException();
        }

        public int Meaning => throw new NotImplementedException();
    }

    // Class we want to test in isolation of ThingDependency
    public class ThingBeingTested
    {
        private readonly IThingDependency _thingDependency;

        public string FirstName { get; set; }
        public string LastName { get; set; }

        public ThingBeingTested(IThingDependency thingDependency)
        {
            _thingDependency = thingDependency;
        }

        public string X()
        {
            var fullName = _thingDependency.JoinUpper(FirstName, LastName);

            return $"{fullName} = {_thingDependency.Meaning}";
        }
    }
}

Without a mock object, to write a test we could use the real ThingDependency:

[Fact]
public void TestUsingRealDependency()
{
    var sut = new ThingBeingTested(new ThingDependency());

    // test code
}

To isolate the ThingBeingTested from the rest of the system, Moq can create a mock version of an IThingDependency:

[Fact]
public void TestUsingMockDependency()
{
    // create mock version
    var mockDependency = new Mock<IThingDependency>();

    // set up mock version's method
    mockDependency.Setup(x => x.JoinUpper(It.IsAny<string>(), It.IsAny<string>()))
                  .Returns("A B");

    // set up mock version's property
    mockDependency.Setup(x => x.Meaning)
                  .Returns(42);

    // create thing being tested with a mock dependency
    var sut = new ThingBeingTested(mockDependency.Object);

    var result = sut.X();

    Assert.Equal("A B = 42", result);
}

In the preceding code, the Setup() method is used to tell the mock how to behave when it is called by the ThingBeingTested.

Moq can also be used to test the correct interactions are occurring between the ThingBeingTested and the IThingDependency:

[Fact]
public void TestUsingMockDependencyUsingInteractionVerification()
{
    // create mock version
    var mockDependency = new Mock<IThingDependency>();

    // create thing being tested with a mock dependency
    var sut = new ThingBeingTested(mockDependency.Object)
    {
        FirstName = "Sarah",
        LastName = "Smith"
    };

    sut.X();

    // Assert that the JoinUpper method was called with Sarah Smith
    mockDependency.Verify(x => x.JoinUpper("Sarah", "Smith"), Times.Once);

    // Assert that the Meaning property was accessed once
    mockDependency.Verify(x => x.Meaning, Times.Once);
}

In the preceding code, the Verify method is used to check that the mock JoinUpper method is being called exactly once with the values “Sarah” and “Smith”. The test code is also expecting the method to be called exactly once.

Moq can be used to test in isolation other parts of applications such as ASP.NET Core MVC controllers, where the controller requires a dependency (such as an IFooRepository):

[Fact]
public void ContollerTest()
{            
    var mockDependency = new Mock<IFooRepository>();

    var sut = new HomeController(mockDependency.Object);
    
    // test code
}

Check out the quickstart for more information on the features of Moq.

To learn how to get started testing ASP.NET Core MVC check out my ASP.NET Core MVC Testing Fundamentals Pluralsight course.

FeatureToggle v4 RC2 with .NET Core Configuration Changes

FeatureToggle logo

The pre-release RC2 version of FeatureToggle with .NET Core support is now available on NuGet.

See release notes and GitHub issues for additional background/breaking changes/limitations.

RC2 builds on RC1 and modifies the format of the json settings to make use of nesting as discussed in this issue, for example:

{
  "FeatureToggle": {
    "Printing": "true",
    "Saving": "false"
  }
}

Thanks to @steventmayer for the suggestions.

Architecting Azure Functions: Function Timeouts and Work Fan-Out with Queues

When moving to Azure Functions or other FaaS offerings it’s possible to fall into the trap of “desktop development’ thinking, whereby a function is implemented as if it were a piece of desktop code. This may negate the benefits of Azure Functions and may even cause function failures because of timeouts. An Azure Function can execute for 5 minutes before being shut down by the runtime when running under a Consumption Plan. This limit can be configured to be longer in the host.json (currently to a mx of 10 minutes). You could also investigate something like Azure Batch.

Non Fan-Out Example

Azure functions flow

In this initial attempt, a blob-triggered function is created that receives a blob containing a data file. Each line has some processing performed on it (simulated in the following code) and then writes multiple output blobs, one for each processed line.

using System.Threading;
using System.Diagnostics;

public static void Run(TextReader myBlob, string name, Binder outputBinder, TraceWriter log)
{
    var executionTimer = Stopwatch.StartNew();

    log.Info($"C# Blob trigger function Processed blob\n Name:{name}");

    string dataLine;
    while ((dataLine = myBlob.ReadLine()) != null)
    {
        log.Info($"Processing line: {dataLine}");
        string processedDataLine = ProcessDataLine(dataLine);
        
        string path = $"batch-data-out/{Guid.NewGuid()}";
        using (var writer = outputBinder.Bind<TextWriter>(new BlobAttribute(path)))
        {
            log.Info($"Writing output line: {dataLine}");
            writer.Write(processedDataLine);
        }
    }

    executionTimer.Stop();

    log.Info($"Procesing time: {executionTimer.Elapsed}");
     
}

private static string ProcessDataLine(string dataLine)
{
    // Simulate expensive processing
    Thread.Sleep(1000);

    return dataLine;
}

Uploading a normal sized input data file may not result in any errors, but if a larger file is attempted then you may get a function timeout:

Microsoft.Azure.WebJobs.Host: Timeout value of 00:05:00 was exceeded by function: Functions.ProcessBatchDataFile.

Fan-Out Example

Embracing Azure Functions more, the following pattern can be used, whereby there is no processing in the initial function. Instead the function just divides up each line of the file and puts it on a storage queue. Another function is triggered from these queue messages and does the actual processing. This means that as the number of messages in the queue grows, multiple instances of the queue-triggered function will be created to handle the load.

Azure functions fan-out flow

public async static Task Run(TextReader myBlob, string name, IAsyncCollector<string> outputQueue, TraceWriter log)
{
    log.Info($"C# Blob trigger function Processed blob\n Name:{name}");

    string dataLine;
    while ((dataLine = myBlob.ReadLine()) != null)
    {
        log.Info($"Processing line: {dataLine}");
        
        string path = $"batch-data-out/{Guid.NewGuid()}";
        
        await outputQueue.AddAsync(dataLine);
    }
}

And the queue-triggered function that does the actual work:

using System;
using System.Threading; 

public static void Run(string dataLine, out string outputBlob, TraceWriter log)
{
    log.Info($"Processing data line: {dataLine}");

    string processedDataLine = ProcessDataLine(dataLine);

    log.Info($"Writing processed line to blob: {processedDataLine}");
    outputBlob = processedDataLine;
}


private static string ProcessDataLine(string dataLine)
{
    // Simulate expensive processing
    Thread.Sleep(1000);

    return dataLine;
}

When architecting processing this way there are other limits which may also cause problems such as (but not limited to) queue scalability limits.

To learn more about Azure Functions, check out my Pluralsight courses: Azure Function Triggers Quick Start  and  Reducing C# Code Duplication in Azure Functions.

Multiple Platform Targeting in Visual Studio 2017

Suppose you are creating a library that has a central set of features and also additional features that are only available on some platforms. This means that when the project is built there are multiple assemblies created, one for each platform.

One way to achieve multi platform targeting is to create a number of separate projects, for example one for .NET Core , one for UWP, another one for .NET framework, etc. Then a shared source code project can be added and referenced by each of these individual projects; when each project is built separate binaries are produced. I’ve used this approach in the past, for example when working on FeatureToggle but is a little clunky and results in many projects in the solution.

Another approach is to have a single project that is not limited to a single platform output, but rather compiles  to multiple platform assemblies.

For example, in Visual Studio 2017, create a new .NET Core class library project called TargetingExample and add a class called WhoAmI as follows:

using System;

namespace TargetingExample
{
    public static class WhoAmI
    {
        public static string TellMe()
        {
            return ".NET Core";
        }
    }
}

After building the following will be created: "…\MultiTargeting\TargetingExample\TargetingExample\bin\Debug\netcoreapp1.1\TargetingExample.dll". Notice the platform directory “netcoreapp1.1”.

If we add a new .NET Core console app project and reference the TargetingExample project:

using System;
using TargetingExample;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(WhoAmI.TellMe());
            Console.ReadLine();
        }
    }
}

This produces the output: .NET Core

If we edit the FeatureToggle.csproj file it looks like the following (notice the TargetFramework element has a single value netcoreapp1.1):

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>
</Project>

The file can be modified as follows (notice the plural <TargetFrameworks>):

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netcoreapp1.1;net461;</TargetFrameworks>
  </PropertyGroup>
</Project>

Building now produces: "…\MultiTargeting\TargetingExample\TargetingExample\bin\Debug\netcoreapp1.1\TargetingExample.dll" and  "…\MultiTargeting\TargetingExample\TargetingExample\bin\Debug\net461\TargetingExample.dll"”.

A new Windows Classic Desktop Console App project can now be added (and the .NET framework version changed to 4.6.1) and a reference to TargetingExample  added.

using System;
using TargetingExample;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(WhoAmI.TellMe());
            Console.ReadLine();
        }
    }
}

The new console app contains the preceding code and when run produces the output: .NET Core.

Now we have a single project compiling for multiple target platforms. We can take things one step further by having different functionality depending on the target platform. One simple way to do this is to use conditional compiler directives as the following code shows:

using System;

namespace TargetingExample
{
    public static class WhoAmI
    {
        public static string TellMe()
        {
#if NETCOREAPP1_1
            return ".NET Core";
#elif NETFULL
            return ".NET Framework";
#else
            throw new NotImplementedException();  // Safety net in case of typos in symbols
#endif
        }
    }
}

The preceding code relies on the conditional compilation symbols being defined, this can be done by editing the project file once again as follows:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netcoreapp1.1;net461;</TargetFrameworks>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.1' ">
    <DefineConstants>NETCOREAPP1_1</DefineConstants>
  </PropertyGroup>
  
  <PropertyGroup Condition=" '$(TargetFramework)' == 'net461' ">
    <DefineConstants>NETFULL</DefineConstants>
  </PropertyGroup>
</Project>

Now when the project is built, the netcoreapp1.1\TargetingExample.dll will return “.NET Core” and net461\TargetingExample.dll will return “.NET Framework”. Each dll has been compiled with different functionality depending on the platform.

Update: The explicit <DefineConstants> for the different platforms are not required if you want to use the defaults, e.g. "NETCOREAPP1_1", "NET461", etc as per this Twitter thread and GitHub.

FeatureToggle v4 RC1 with .NET Core Support

The pre-release RC1 version of FeatureToggle with .NET Core support is now available on NuGet.

See release notes and GitHub issues for additional background/breaking changes/limitations.

The main drive for v4 is to add initial .NET Core support.

Using Feature Toggle in a .NET Core Console App

In Visual Studio 2017, create a new .NET Core Console App and install the NuGet package. This will also install the dependent FeatureToggle.NetStandard package (.NET Standard 1.4).

Add the following code to Program.cs:

using System;
using FeatureToggle;

namespace ConsoleApp1
{

    public class Printing : SimpleFeatureToggle { }
    public class Saving : EnabledOnOrAfterDateFeatureToggle { }
    public class Tweeting : EnabledOnOrAfterAssemblyVersionWhereToggleIsDefinedToggle { }

    class Program
    {
        static void Main(string[] args)
        {
            var p = new Printing();
            var s = new Saving();
            var t = new Tweeting();

            Console.WriteLine($"Printing is {(p.FeatureEnabled ? "on" : "off")}");
            Console.WriteLine($"Saving is {(s.FeatureEnabled ? "on" : "off")}");
            Console.WriteLine($"Tweeting is {(t.FeatureEnabled ? "on" : "off")}");


            Console.ReadLine();
        }
    }
}

Running the application will result in an exception due to missing appSettings.config file: “System.IO.FileNotFoundException: 'The configuration file 'appSettings.json' was not found and is not optional.“ By default, FeatureToggle will expect toggles to be configured in this file, add an appSettings.json and set its Copy To Output Directory to “Copy if newer” and add the following content:

{
  "FeatureToggle.Printing": "true",
  "FeatureToggle.Saving": "01-Jan-2014 18:00:00",
  "FeatureToggle.Tweeting": "2.5.0.1" // Assembly version is set to 2.5.0.0
}

Running the app now result in:

Printing is on
Saving is on
Tweeting is off

Using Feature Toggle in an ASP.NET Core App

Usage in an ASP.NET core app currently requires the configuration to be provided when instantiating a toggle, this may be cleaned up in future versions. For RC1 the following code shows the Startup class creating a FeatureToggle AppSettingsProviderand and passing it the IConfigurationRoot from the startup class.

public void ConfigureServices(IServiceCollection services)
{
    // Set provider config so file is read from content root path
    var provider = new AppSettingsProvider { Configuration = Configuration };

    services.AddSingleton(new Printing { ToggleValueProvider = provider });
    services.AddSingleton(new Saving { ToggleValueProvider = provider });

    // Add framework services.
    services.AddMvc();
}

The appSettings would look something like the following:

{
  "FeatureToggle.Printing": "true",
  "FeatureToggle.Saving": "false",
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}

As an example of using this configuration check out the example project on GitHub, in particular the following:

https://github.com/jason-roberts/FeatureToggle/blob/master/src/Examples/AspDotNetCoreExample/Models/Printing.cs

https://github.com/jason-roberts/FeatureToggle/blob/master/src/Examples/AspDotNetCoreExample/Models/Saving.cs

https://github.com/jason-roberts/FeatureToggle/blob/master/src/Examples/AspDotNetCoreExample/ViewModels/HomeIndexViewModel.cs

https://github.com/jason-roberts/FeatureToggle/blob/master/src/Examples/AspDotNetCoreExample/Controllers/HomeController.cs

https://github.com/jason-roberts/FeatureToggle/blob/master/src/Examples/AspDotNetCoreExample/Views/Home/Index.cshtml

https://github.com/jason-roberts/FeatureToggle/blob/master/src/Examples/AspDotNetCoreExample/Startup.cs

https://github.com/jason-roberts/FeatureToggle/blob/master/src/Examples/AspDotNetCoreExample/appsettings.json

New Pluralsight Course: Reducing C# Code Duplication in Azure Functions

Azure Functions allow small discrete pieces of code to execute in response to an external stimulus such as a HTTP request, message queue message, new blob data, etc.

Just because functions are easy to create (even writing and testing code right in the Azure Portal) doesn’t mean good practices such as avoiding code duplication can be abandoned.

My new Pluralsight course Reducing C# Code Duplication in Azure Functions shows some ways to reduce or remove code duplication both in a single Function App and across apps.

Free eBook C# 7.0: What’s New Quick Start Complete

My new free eBook “C# 7.0: What’s New Quick Start” is now complete and available for download.

C# 7.0: What’s New Quick Start eBook Cover Image

The book covers the following:

  • Literal Digit Separators and Binary Literals
  • Throwing Exceptions in Expressions
  • Local Functions
  • Expression Bodied Accessors, Constructors and Finalizers
  • Out Variables
  • By-Reference Local Variables and Return Values
  • Pattern Matching
  • Switch Statements
  • Tuples

You can download it for free or pay what you think it is worth.

Happy reading!

New Pluralsight Course: Azure Function Triggers Quick Start

Azure Functions allow small discrete pieces of code to execute in response to an external stimulus such as a HTTP request, message queue message, new blob data, etc. They can also be triggered manually from within the Azure Portal or set to execute on a specified schedule.

My new Azure Function Triggers Quick Start  Pluralsight course shows how to get up to speed quickly with each of the function trigger types such as:

  • Manual Triggers
  • Azure Queue Storage Triggers
  • Blob Triggers
  • Timer Triggers
  • HTTP Triggers
  • Webhook Triggers
  • Service Bus Triggers
  • Event Hub Triggers

You can check out the course here.

Creating Versioned APIs with Azure Functions and Proxies

One of the interesting possibilities with the (currently in preview) Azure Function Proxies is the ability to create HTTP APIs that can be versioned and also deployed/managed independently.

For example, suppose there is a API that lives at the root “https://dctdemoapi.azurewebsites.net/api". We could have multiple resources under this root such as customer, products, etc.

So to get the product with an id of 42 we’d construct: “https://dctdemoapi.azurewebsites.net/api/products?id=42”.

If we wanted the ability to version the API we could construct “https://dctdemoapi.azurewebsites.net/api/v1/products?id=42” for version 1 and “https://dctdemoapi.azurewebsites.net/api/v2/products?id=42” for version 2, etc.

Using proxies we can use the format “https://dctdemoapi.azurewebsites.net/api/[VERSION]/[RESOURCE]?[PARAMS]”

Now we can create 2 proxies (for example in an Azure Function called “dctdemoapi”) that forwards the HTTP requests to other Function Apps (in this example dctdemoapiv1 and dctdemoapiv2).

Screenshots of the proxies are as follows:

Azure Function proxy settings for API version 1

Azure Function proxy settings for API version 2

And the respective proxies.json config file:

{
    "proxies": {
        "v1": {
            "matchCondition": {
                "route": "api/v1/{*restOfPath}"
            },
            "backendUri": "https://dctdemoapiv1.azurewebsites.net/api/{restOfPath}"
        },
        "v2": {
            "matchCondition": {
                "route": "api/v2/{*restOfPath}"
            },
            "backendUri": "https://dctdemoapiv2.azurewebsites.net/api/{restOfPath}"
        }
    }
}

Notice in the proxy config the use of the wildcard term “{*restOfPath}” – this will pass the remainder of the path segments to the backend URL, for example “products”, meaning a request to “https://dctdemoapi.azurewebsites.net/api/v1/products?id=42” will be sent to “https://dctdemoapiv1.azurewebsites.net/api/products?id=42”; and “https://dctdemoapi.azurewebsites.net/api/v2/products?id=42” will be sent to “https://dctdemoapiv2.azurewebsites.net/api/products?id=42”.

Now versions of the API can be updated/monitored/managed/etc independently because they are separate Function App instances, but code duplication is a potential problem; common business logic could however be compiled into an assembly and referenced in both Function Apps.

To jump-start your Azure Functions knowledge check out my Azure Function Triggers Quick Start Pluralsight course.