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.
Please consider using Brave and adding me to your BAT payment ledger. Then you won't have to see ads! (when I get to $100 in Google Ads for a payout (I'm at $95.73!), I pledge to turn off ads)
Also check out my Resources Page for referrals that would help me.