Geeks With Blogs

News Please visit me at my new blog!!

profile for Aligned at Stack Overflow, Q&A for professional and enthusiast programmers
"free in Christ Jesus from the law of sin and death." Romans 8:2 (ESV) Check out the Falling Plates video on YouTube.
more about the Gospel
And then listen to Francis Chan speaking at LifeLight in SD.



Programming and Learning from SD

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.

  1. Read the MSDN Unit Testing MVC article.
    1. consider splitting controllers into a different project as suggested
    2. Don’t put your data access code inside the Controller methods, use a data layer/object. Create something you can inject and mock.
  2. Create an MVC project.
  3. Use Ninject to setup Dependency Injection
  4. Get the Ninject WebAPI NuGet package, if using WebApi so DI will work with WebApi.
  5. Create a solution folder in Visual Studio with this structure.
    1. Tests
      1. Integration
      2. Performance
      3. UI
      4. Unit
  6. Decision: unit testing driven or outside in BDD (my benefits of BDD article)/SpecFlow testing, possibly with Selenium. (Probably both).
  7. Create a testing project (named {WebsiteName}.Controllers.Tests) under Unit.
  8. Use a mocking tool. I like FakeItEasy, but Moq is very good as well. Get it from NuGet.
  9. Setup the tests for DI. This is one approach I’ve used that seems pretty straight forward.
    1. Create a file named SetupDiFOrTests in your test project. This will setup dependencies and expose the fake services (data access) as a static property.
      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; }
      }
    2. 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.Models;
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.

Posted on Thursday, September 25, 2014 9:18 PM MVC , Unit Testing , BDD , Inversion Of Control (Ioc) , Mocking , Dependency Injection , Pragmatic Programming | Back to top


Comments on this post: Quick Guide to setup a MVC project for Unit Testing

# re: Quick Guide to setup a MVC project for Unit Testing
Requesting Gravatar...
This article is very educative and informative for the new learner.it is unique article
Left by mamun on Sep 26, 2014 7:06 AM

Your comment:
 (will show your gravatar)


Copyright © Aligned | Powered by: GeeksWithBlogs.net