Originally posted on GeeksWithBlogs.net on September 25, 2014. This was written before Asp .Net Core.
A barrier to getting into writing executable tests (Unit Tests, integration tests, automated UI tests, etc) with some people I work with, is not knowing how to get started. I believe the desire and the recognition of the value and importance of this testing is growing, but I want to help people get over that hump.
public class SetupDIForTest
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
public static void Start()
{
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
RegisterServices(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
FakeLogger = A.Fake<ILogger>();
kernel.Bind<ILogger>().ToMethod((x) => FakeLogger);
var fakeContext = A.Fake<ITimeSheetDbContext>();
kernel.Bind<ITimeSheetDbContext>().ToMethod((x) => fakeContext);
FakeTimeSheetService = A.Fake<ITimeSheetService>();
kernel.Bind<ITimeSheetService>().ToMethod((x) => FakeTimeSheetService);
}
public static ILogger FakeLogger { get; private set; }
public static ITimeSheetService FakeTimeSheetService { get; private set; }
}
1. Call the setup start method from an assembly init and add in tests. See my example test method of a Timesheet application.
[TestClass]
public class When_Creating_TimeSheets
{
[AssemblyInitialize]
public static void AssemblyInit(TestContext testContext)
{
SetupDIForTest.Start();
}
[TestMethod]
public void It_Should_Populate_The_Users_Select()
{
// Arrange
// use FakeItEasy to set what the method will return
A.CallTo(() => SetupDIForTest.FakeTimeSheetService.GetTimeSheetUsersAsync())
.Returns(Task.FromResult(new List<TimeSheetUser>{
new TimeSheetUser{
FirstName = "Kevin"
},
new TimeSheetUser{
FirstName = "Terry"
}
}.AsEnumerable()));
// Act
var controller = new TimeSheetEntriesController(SetupDIForTest.FakeTimeSheetService, SetupDIForTest.FakeLogger);
controller.Create();
// Assert
var selectList = controller.ViewBag.TimeSheetUserID as SelectList;
Assert.IsNotNull(selectList);
Assert.AreEqual("Kevin", selectList.First().Text, "First user should be Kevin");
Assert.AreEqual("Terry", selectList.Skip(1).First().Text);
Assert.AreEqual(2, selectList.Count(), "It should fill the list with all users from GetTimeSheetUsers");
}
}
That should be enough to get your writing some unit tests against your controllers. Happy testing!
EDIT from December 9th, 2015. There is a Ninject.MockingKernel.FakeItEasy Nuget library that simplifies things.
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Acme.Services;
using Acme.Web.Controllers;
using FakeItEasy;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Ninject;
using Ninject.MockingKernel;
using Ninject.MockingKernel.FakeItEasy;
namespace Acme.Web.Tests
{
[TestClass]
public class ProductControllerTests
{
private readonly FakeItEasyMockingKernel _kernel;
public ProductControllerTests()
{
_kernel = new FakeItEasyMockingKernel();
_kernel.Bind<IProductService>().ToMock();
}
[TestInitialize]
public void TestInit()
{
_kernel.Reset();
}
[TestMethod]
[TestCategory("Product Controller Tests")]
public void Index_OrderedByProductName()
{
// Arrange
var testData = new List<Product>
{
new Product {ProductId = 1, Name = "Z Product 1", Description = "this is a description", Active = true},
new Product {ProductId = 4, Name = "A Product 4", Description = "this is a description", Active = true},
};
var productServiceMock = _kernel.Get(typeof (IProductService)) as IProductService;
A.CallTo(() => productServiceMock.GetActiveProducts()).Returns(testData);
var controller = new ProductsController(productServiceMock);
// Act
var results = (ViewResult) controller.Index();
// Assert
var model = (List<Product>) results.ViewData.Model;
Assert.AreEqual(testData[0].Name, model.Last().Name, "Should be sorted by first name");
}
}
}
P.S.
You may have to do more advanced mocking of the user or other HTTP objects in MVC to get good coverage.
There is a very good course on Pluralsight about Executable Specifications if you’re interested in SpecFlow, when thinking about testing.
Check out my Resources Page for referrals that would help me.