Wednesday, May 8, 2013

MVC4 TDD, IoC (by Unity) and Mocking (by FakeItEasy) - Start Thinking in Abstractions and Fake it without implementation by writing minimum code

In this article we review how to create a simple MVC 4 application using a Test Driven Development (TDD) approach. We'll use the Inversion of Control (IoC) pattern, along with Microsoft's Unity Dependency Injection (DI) container and the FakeItEasy (he easy mocking library for .NET) framework. We start by considering the typical Red, Green, Refactor TDD cycle, and describe how  IoC and mocking are a desirable and logical consequence of TDD. We start from Functional Requirement and conceptual model and start writing tests first of all.  Then define interfaces, blank entities, services and enforce business logic, validation, logging much more – all at abstraction level without any implementation. Its remarkable.

TDD is not a testing methodology; it's a design and development methodology!

It is nice to know variant views about TDD. ‘TDD is not a good design methodology.’ You can read popular article at The TDD Apostate. Let us not go into details whether TDD results in good design or not, TDD is still accepted, widely liked and remain would be alive in future.

I read this article before decided to write similar thing with ‘FakeItEasy’ mocking library and slightly different mind.  

TDD emphasizes an iterative development cycle where requirements are created in the form of unit tests, and each test is written first - before the code that defines interfaces, services, business logic, or UI. Typically, for each work item (e.g. a particular new feature) the developer will use a three-phase (Red, Green, and Refactor) cycle, with each cycle being of short duration (e.g. an hour):
  • Red: write a unit test that fails
    • First, make sure you understand the requirements for the work item
    • Design/imagine how you'd like the feature to be implemented, then write the test code as though the code existed
    • Create just the necessary interfaces and 'stubs' (or use a Mocking framework - see below) so the test code compiles
    • Run the test - it will fail because the 'real code' has not yet been written. However, this verifies our mocked code isn't working by accident, which could potentially provide a 'false positive' at a later stage in development
  • Green: make the minimum changes required to pass the test
    • add the minimum code required to make the test pass - make use of mocking or hard-code method return values
    • don't add error handling code - do this later, again driven by specific tests
    • re-run all unit tests (regression testing) to ensure you haven't broken anything
  • Refactor: improve design, add business and data persistence logic, etc.
    • gradualy replace stubbed or mocked code with real-world implementation logic
    • improve the design, etc.
    • re-run tests to ensure eveything still works
It's interesting to note that this deceptively simple approach actually encapsulates a major change in the way we approach the task of writing software. Normally we ask the question, "How will I write the code to create the solution?". With TDD the question becomes, "How will I know I've solved the problem?". As J. Timothy King says in his article Twelve Benefits of Writing Unit Tests First: "We're taught to assume we already know how to tell whether our solution works. It's a non-question. Like indecency, we'll know it when we see it. We believe we don't actually need to think, before we write our code, about what it needs to do. This belief is so deeply ingrained, it's difficult for most of us to change."
There is a complete mental shift as thinking with TDD approach for developers, designers and architects. Instead of thinking in three basic traditional steps: design, implement, and test, we must build and test small components over time and improve with refactoring – thus lead us to create a system architecture, what benefits are we likely to see?
  • Design: being able to run automated unit tests forces us up-front to design a system composed of loosely-coupled components (e.g. separation of concerns)
  • Reassurance: re-running the unit tests assures us that any change we make hasn't broken anything
  • Team-working: re-running all tests (e.g. at the end of the day) is a good way of picking up any breaking-changes at an early stage
  • Documentation: tests actually document how the system works (and they can't be out-of-date)
  • Metrics: tests provide a practical measure of progress (e.g. "the system passes 78 of 100 tests")
Obviously, the first step is to capture the functional requirements that define what the system must do and the Quality of Service (QoS) requirements that define how well it must perform. 

Problems with Conventional Approach
Conventional methods divide the development process into three basic steps: design, implement, and test. The problem with this approach is that the two types of strategic defects, those related to requirements and architecture, are often introduced early but detected late. Of all defects, these two are the most costly because they can significantly affect most or all of the system.

If tested primarily at the end of the development cycle, the system is likely to include defects with complex design interdependencies, making their identification and removal expensive, error-prone, and time-consuming. These strategic defects are often not identified until the validation phase when they might have thousands of subtle dependencies based on assumptions that the fix invalidates. Finding these dependencies and repairing them can be difficult.

TDD solves this problem by building and testing small system components over time. With TDD, testing is not dealt with all at once toward the end of the development cycle. Instead, it is performed incrementally throughout the development life cycle to ensure that the system works as specified at every phase.

This incremental approach requires that developers test a project at every stage of its development, meaning that the system must be constructed from executable and testable models. Scenarios specified during requirements capture are used in downstream development to show that models are correct and meet the requirements. The goal is to identify strategic defects as early as possible; these defects can cost up to 1,000 times more than coding defects if they are discovered late in the project. A good rule of thumb is that no more than 5 percent of defects should be identified in the last 10 percent of the project life cycle.

TDD in Action - an MVC 4 Demo App
To look at TDD in practice, we'll develop a very simple "Subscription" application using the following:
  • Microsoft Visual Studio 2012 with the MVC 4
  • Microsoft Entity Framework 4 (we'll get the latest version using the NuGet Package Manager in Visual Studio)
  • Microsoft Unity dependency injection container (we'll get it using NuGet)
  • The FakeItEasy mocking framework (we'll get it using NuGet)
Our simple 'MvcOnlineStoreApp' will be iteratively developed with simple set of functional requirements as follows:
  • There will be a simple home page with a link that takes the user to another page that displays a list of categories
  • A Category must have a descriptive name.
  • Initially, units tests will use the mocking framework to return the list of categories.
  • We'll use the repository pattern to de-couple the data persistence layer from the controller
  • The dependency injection container will be used 
To get started, open Visual Studio and create a new MVC 4 project (and when prompted, choose the 'Empty' template) - I named my project 'MvcOnlineStoreApp':

In next screen, select template ‘Internet Application’ and check ‘Create a unit test project’ option to true.
Move both projects (MvcOnlineStoreApp and MvcOnlineStoreApp.Tests) under a logical forlder named ‘UI’.
Let us create two more logical folders named ‘Domain’ and ‘OnlineShop’.
Create four library projects under ‘Domain’ folder.

  1. Domain.Entity: Contain dummy DTO classes like Category.
  2. Domain.Infrastructure: Contain common interfaces used across application
  3. Domain.Util.Infrastructure: Contain interfaces used for Utility classes
  4. Domain.Util.Implementation: Contain implementation of Utility interfaces

Now consider ‘OnlineShop’ conceptual box may contain some abstract services. We must start creating empty WebApi services having no display functionality. Think of following services in Online Shop.
-          CategoryService
-          ProductService
-          PartyService
-          OnlinePurchaseService
-          OrderService
Right click on ‘OnlineShop’ folder and add new project. Select ‘ASP.NET MVC 4 Web Application’ template, click browse button next to Location text box and create a folder named ‘OnlineShop’.

 Next step us to select ‘Web Api’ and click OK button. Two projects are created.
Delete following four folders because we never use this content in back-end service.

Under ‘Controllers’ folder delete Home and value controllers because we never use them. Similarly delete a test controller under ‘Controllers’ folder in project ‘ProductService.Tests’.
Build the project now, it should run without errors.
Add new controller named ‘CategoryController’ as follows.

Add a new interface named ‘ILoggerService’ under ‘Domain.Infrastructure’ project. I know its is responsible of loggin information and errors somewhere, we don’t know yet. But let us assume we know nothing until test will drive us to add methods in it.

Keep it simple:
Go to project ‘Domain.Enitiy’ and create new class named ‘Category’. Uth is we only know Category contains a Name. So code looks like this.

Go to ‘ProductService.Tests’ folder and new plain class under ‘Controllers folder as:

Now in same project install NuGet packages ‘FakeItEasy’ and ‘Unity’ as follows:

Now add references to three more projects to the project ‘ProductService.Tests’ as follows:

Let us add some code in test class to look like this.

Above code contains [TestInitialize] method that is executed at start. I tried to create Fake objects of ICategoryService and ILoggerServcie. And pack the CategoryController object in Unity container to use it in further test cases.
So I need to create two things.
      1 - Need to create ICategoryService Interface  (proper place should be Domain.Infrastructure project)
2 - CategoryController parameterized constructor to inject ICategoryServcie and ILoggerServcie dependencies

Point 1: Letus add ICategoryServcie and a ListCategories method as well (I should not add it until I need it in tests but let’s do this way for now)

I shifted my scope by assuming I should for the time being use Repository ‘ICategoryRepository’ instead of IRepositoryService. So I added bit of code as follows.

Add two methods under ILoggerServcie interface. (again I should add them after writing test but let us do it for now – sorry violating TDD)

Point2: Create a constructor now in ‘CategoryController’       

Let us write first basic test that fails passing Null as first parameter to ‘CategoryController’

Let us try to pass it. I added two lines in contructor and run test – it passes.

Reason to fail was because test was expecting ArgumentNullException when passed NULL as first parameter to constructor.
I added some more three tests same way and refactored the Contoller code accordingly, so final code looks like this:

public void Initializing_CategoryController_with_Null_ICategoryRepository_throws_ArgumentNullException()
    var sut = new CategoryController(categoryService: null, loggerService: A.Fake<ILoggerService>());

public void Initializing_CategoryController_with_Valid_ICategoryRepository_sets_CategoryService_property_to_provided_value()
    var categoryService = A.Fake<ICategoryRepository>();
    var sut = new CategoryController(categoryService: categoryService, loggerService: A.Fake<ILoggerService>());

    Assert.AreSame(categoryService, sut.CategoryService);

public void Initializing_CategoryController_with_Null_ILoggerService_throws_ArgumentNullException()
    var sut = new CategoryController(categoryService: A.Fake<ICategoryRepository>(), loggerService: null );

public void Initializing_CategoryController_with_Valid_ILoggerService_sets_CategoryService_property_to_provided_value()
    var loggerService = A.Fake<ILoggerService>();
    var sut = new CategoryController(categoryService: A.Fake<ICategoryRepository>(), loggerService: null);

    Assert.AreSame(loggerService, sut.LoggerService);

And final shape of CategoryController looks like this:

public class CategoryController : ApiController
    public ILoggerService LoggerService { get; private set; }
    public ICategoryRepository CategoryService { get; private set; }

    public CategoryController(ICategoryRepository categoryService, ILoggerService loggerService)
        if (categoryService == null)
            throw new ArgumentNullException();
        if (loggerService == null)
            throw new ArgumentNullException();

        CategoryService = categoryService;
        LoggerService = loggerService;


My four tests are now succeeded.

Let us implement First requirement now “To Fetch List of Categories From whatever (aka DB)”

Wrote my first requirement test code as follows. It is success scenario fetching categories list.

The catch is, we need to implement sut.ListCategories method in the CategoryController class. (sut = system under test)
Here is the method implementation:
Run all the tests again on CategoryControllerTests class and you will get all of them succeeded.

At the end – you see how we deal in abstractions and write tests using FakeItEasy library. It is remarkable to have no implementation but sufficient code coverage. It shows my code is well written and bug free in very start. We can always start from Functional requirements, conceptual model and start writing system architecture in Visual Studio with all necessary components in mind. We know it will continuously change with requirements but at least we have pretty solid ground to start with every changing requirements especially we know less in the beginning.
I will discuss the concept more in next posts but if you need running sample please DOWNLOAD from skydrive. (FileName:

No comments:

Post a Comment