MY TESTED ASP.NET CORE MVC DOCS
  • Organizing Tests
Show / Hide Table of Contents
  • Introduction
  • Getting Started
  • Packages
  • Debugging Failed Tests
  • Controllers
  • Models
  • Database
  • Services
  • HTTP & Authentication
  • Licensing
  • Attributes
  • Exceptions
  • Options
  • Session & Cache
  • ViewBag, ViewData & TempData
  • View Components
  • Routing
  • Various Helpers
  • Organizing Tests
  • Test Configuration
  • Extension Methods

Organizing Tests

This section will cover the various ways we can organize our tests in a single class.

The fluent style

This is the test style we used so far in the tutorial. For example, let's take a look at our "StoreControllerTest" class:

namespace MusicStore.Test.Controllers
{
    using Microsoft.Extensions.Options;
    using Moq;
    using MusicStore.Controllers;
    using MusicStore.Models;
    using MusicStore.Test.Mocks;
    using MyTested.AspNetCore.Mvc;
    using System.Collections.Generic;
    using Xunit;

    public class StoreControllerTest
    {
        [Fact]
        public void IndexShouldReturnViewWithGenres()
            => MyController<StoreController>
                .Instance()
                .WithData(
                    new Genre { Name = "FirstGenre" },
                    new Genre { Name = "SecondGenre" })
                .Calling(c => c.Index())
                .ShouldReturn()
                .View(result => result
                    .WithModelOfType<List<Genre>>()
                    .Passing(model => model.Count == 2));
        [Fact]
        public void BrowseShouldReturnNotFoundWithInvalidGenre()
            => MyController<StoreController>
                .Instance(new StoreController(
                    MockProvider.MusicStoreContext,
                    Mock.Of<IOptions<AppSettings>>()))
                .Calling(c => c.Browse("Invalid"))
                .ShouldReturn()
                .NotFound();

        [Fact]
        public void BrowseShouldReturnCorrectViewModelWithValidGenre()
            => MyController<StoreController>
                .Instance()
                .WithDependencies(
                    MockProvider.MusicStoreContext,
                    From.Services<IOptions<AppSettings>>())
                .Calling(c => c.Browse("Rap"))
                .ShouldReturn()
                .View(result => result
                    .WithModelOfType<Genre>()
                    .Passing(model => model.GenreId == 2));
    }
}

The classic AAA style

You can split the fluent API in the classic Arrange-Act-Assert style:

[Fact]
public void IndexShouldReturnViewWithGenres()
{
    // Arrange
    var controller = MyController<StoreController>
        .Instance()
        .WithData(
            new Genre { Name = "FirstGenre" },
            new Genre { Name = "SecondGenre" });

    // Act
    var execution = controller.Calling(c => c.Index());

    // Assert
    execution
        .ShouldReturn()
        .View(result => result
            .WithModelOfType<List<Genre>>()
            .Passing(model => model.Count == 2));
}

The fluent AAA style

Or just mark the fluent API with comments:

[Fact]
public void IndexShouldReturnViewWithGenres()
    => MyController<StoreController>
        // Arrange
        .Instance()
        .WithData(
            new Genre { Name = "FirstGenre" },
            new Genre { Name = "SecondGenre" })
        // Act
        .Calling(c => c.Index())
        // Assert
        .ShouldReturn()
        .View(result => result
            .WithModelOfType<List<Genre>>()
            .Passing(model => model.Count == 2));

The inheriting style

You may inherit the "MyController" class to skip writing it in every single test:

namespace MusicStore.Test.Controllers
{
    using Microsoft.Extensions.Options;
    using Mocks;
    using Models;
    using Moq;
    using MusicStore.Controllers;
    using MyTested.AspNetCore.Mvc;
    using System.Collections.Generic;
    using Xunit;

    public class StoreControllerTest : MyController<StoreController> // <---
    {
        [Fact]
        public void IndexShouldReturnViewWithGenres()
            => this
                .WithData(
                    new Genre { Name = "FirstGenre" },
                    new Genre { Name = "SecondGenre" })
                .Calling(c => c.Index())
                .ShouldReturn()
                .View(result => result
                    .WithModelOfType<List<Genre>>()
                    .Passing(model => model.Count == 2));

        [Fact]
        public void BrowseShouldReturnNotFoundWithInvalidGenre()
            => Instance(new StoreController(
                    MockProvider.MusicStoreContext,
                    Mock.Of<IOptions<AppSettings>>()))
                .Calling(c => c.Browse("Invalid"))
                .ShouldReturn()
                .NotFound();

        [Fact]
        public void BrowseShouldReturnCorrectViewModelWithValidGenre()
            => this
                .WithDependencies(
                    MockProvider.MusicStoreContext,
                    Mock.Of<IOptions<AppSettings>>())
                .Calling(c => c.Browse("Rap"))
                .ShouldReturn()
                .View(result => result
                    .WithModelOfType<Genre>()
                    .Passing(model => model.GenreId == 2));
    }
}

NOTE: To run the above tests asynchronously, the test runner should instantiate the "StoreControllerTest" class for every single test. This is the default behavior of "xUnit" so you shouldn't experience any issues if you do not alter its collection parallelism. You can also avoid a race condition if you replace the "this" keyword with "Instance":

namespace MusicStore.Test.Controllers
{
    using Microsoft.Extensions.Options;
    using Mocks;
    using Models;
    using Moq;
    using MusicStore.Controllers;
    using MyTested.AspNetCore.Mvc;
    using System.Collections.Generic;
    using Xunit;

    public class StoreControllerTest : MyController<StoreController> // <---
    {
        [Fact]
        public void IndexShouldReturnViewWithGenres()
            => Instance() // <---
                .WithData(
                    new Genre { Name = "FirstGenre" },
                    new Genre { Name = "SecondGenre" })
                .Calling(c => c.Index())
                .ShouldReturn()
                .View(result => result
                    .WithModelOfType<List<Genre>>()
                    .Passing(model => model.Count == 2));

        [Fact]
        public void BrowseShouldReturnNotFoundWithInvalidGenre()
            => Instance(new StoreController(
                    MockProvider.MusicStoreContext,
                    Mock.Of<IOptions<AppSettings>>()))
                .Calling(c => c.Browse("Invalid"))
                .ShouldReturn()
                .NotFound();

        [Fact]
        public void BrowseShouldReturnCorrectViewModelWithValidGenre()
            => Instance() // <---
                .WithDependencies(
                    MockProvider.MusicStoreContext,
                    Mock.Of<IOptions<AppSettings>>())
                .Calling(c => c.Browse("Rap"))
                .ShouldReturn()
                .View(result => result
                    .WithModelOfType<Genre>()
                    .Passing(model => model.GenreId == 2));
    }
}

Section summary

Of course, you can always combine two or more of the above styles as long as your code is consistent. Now, let's take a look at the framework's Test Configuration!

  • Improve this Doc
In This Article
  • The fluent style
  • The classic AAA style
  • The fluent AAA style
  • The inheriting style
  • Section summary
Back to top Copyright © 2015-2016 MyTestedASP.NET. All Rights Reserved. Generated by DocFX