Testing using xUnit

In this section we will learn to write unit and integration tests for our controllers. There are a number of options available to us for choosing the test framework. We will use xUnit for all our unit tests and Moq for mocking objects. Let's create a xUnit test project by doing the following:

  1. Open the Let's Chat project in Visual Studio 2017
  2. Create a new folder named  Test
  3. Right-click the Test folder and click Add | New Project
  4. Select xUnit Test Project (.NET Core) under Visual C# project templates, as shown here:
  1. Delete the default test class that gets created with the template
  2. Create a test class inside this project AuthenticationControllerUnitTests for the unit test

We need to add some NuGet packages. Right-click the project in VS 2017 to edit the project file and add the references manually, or use the NuGet Package Manager to add these packages:

// This package contains dependencies to ASP.NET Core 
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
// This package is useful for the integration testing, to build a test host for the project to test.
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" />
// Moq is used to create fake objects
<PackageReference Include="Moq" Version="4.7.63" />

With this, we are now ready to write our unit tests. Let's start doing this, but before we do that, here's some quick theory about xUnit and Moq. 

The documentation from the xUnit website and Wikipedia tells us that xUnit.net is a free, open source, community-focused unit testing tool for the .NET Framework. It is the latest technology for unit testing C#, F#, Visual Basic .NET, and other .NET languages. All xUnit frameworks share the following basic component architecture:

  • Test runner: It is an executable program that runs tests implemented using an xUnit framework and reports the test results.
  • Test case: It is the most elementary class. All unit tests are inherited from here.
  • Test fixtures: Test fixures (also known as a test context) are the set of preconditions or state needed to run a test. The developer should set up a known good state before the tests, and return to the original state after the tests.
  • Test suites: It is a set of tests that all share the same fixture. The order of the tests shouldn't matter.

xUnit.net includes support for two different major types of unit test: 

  1. Facts: Tests which are always true. They test invariant conditions, that is, data-independent tests.
  2. Theories: Tests which are only true for a particular set of data.

Moq is a mocking framework for C#/.NET. It is used in unit testing to isolate the class under test from its dependencies, and ensure that the proper methods on the dependent objects are being called. 

Recall that in unit tests, we only test a unit or a layer/part of the software in isolation and hence do not bother about external dependencies, so we assume they work fine and just mock them using the mocking framework of our choice.

Let's put this theory into action by writing a unit test for the following action in AuthenticationController:

    public class AuthenticationController : Controller
{
private readonly ILogger<AuthenticationController> logger;

public
AuthenticationController(ILogger<AuthenticationController>
logger)
{
this.logger = logger;
}

[Route("signin")]
public IActionResult SignIn()
{
logger.LogInformation($"Calling {nameof(this.SignIn)}");
return Challenge(new AuthenticationProperties { RedirectUri
= "/" });
}
}

The unit test code depends on how the method to be tested is written. To understand this, let's write a unit test for a SignIn action. To test the SignIn method, we need to invoke the SignIn action in AuthenticationController. To do so, we need an instance of the AuthenticationController class, on which the SignIn action can be invoked. To create the instance of AuthenticationController, we need a logger object, as the AuthenticationController constructor expects it as a parameter. Since we are only testing the SignIn action, we do not bother about the logger and so we can mock it. Let's do it: 

    /// <summary>
/// Authentication Controller Unit Test - Notice the naming
convention {ControllerName}Test
/// </summary>
public class AuthenticationControllerTest
{
/// <summary>
/// Mock the dependency needed to initialize the controller.
/// </summary>
private Mock<ILogger<AuthenticationController>> mockedLogger =
new Mock<ILogger<AuthenticationController>>();

/// <summary>
/// Tests the SignIn action.
/// </summary>
[Fact]
public void SignIn_Pass_Test()
{
// Arrange - Initialize the controller. Notice the mocked
logger object passed as the parameter.
var controller = new
AuthenticationController(mockedLogger.Object);

// Act - Invoke the method to be tested.
var actionResult = controller.SignIn();

// Assert - Make assertions if actual output is same as
expected output.
Assert.NotNull(actionResult);
Assert.IsType<ChallengeResult>(actionResult);
Assert.Equal(((ChallengeResult)actionResult).
Properties.Items.Count, 1);
}
}

Reading the comments would explain the unit test code. The previous example shows how easy it is to write a unit test. Through depending upon the method to be tested, things can get complicated, but most of it would be around mocking the objects and, with some experience on the mocking framework and binging around, mocking should not be a difficult task. The unit test for the SignOut action would be a bit complicated in terms of mocking as it uses HttpContext. The unit test for the SignOut action is left to the reader as an exercise. Let's explore a new feature introduced in Visual Studio 2017 called Live Unit Testing.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset