Google
 

Thursday, June 07, 2007

How design patterns can help you in developing unit testing-enabled applications

REF: http://www.codeproject.com/useritems/DesignPatternsForUnitTest.asp

a few of TDD principles:

  • Do Simple Things: programmers should do the simplest thing that could possibly work.
  • Code Unit Test First: programmers should always code automated tests before coding the functionality that will be tested.
  • Once and Only Once: programmers should sistematically refactor their code to avoid duplication and allow reusability.

  • Some benefits of automated unit tests:

  • Maintainability: Just like the application code, automated unit tests are written in a set of classes, usually maintained in test projects and mapped to the classes to be tested, in an one-by-one basis.
  • Reusability: Automated unit tests can be extended and executed as many times as the development team wishes. Besides, the successful execution of the unit tests can be a requirement for the pre-build phase.
  • Visibility: Unit tests can be used by a code coverage tool so that developers can see how much of the application code has been tested by the unit tests.
  • Developer's Confidence: If a code is properly tested and the automated tests can be easily executed, the developer can refactor or implement new code with much more confidence.
  • Documentation: Unit tests can help extending the class documentation, and providing practical examples of its correct usage.

  • Unfortunately, there are many pitfalls in the path, and the coding unit tests can be a very hard (if not impossible) task, due to bad design choices made by merely applying to new projects the same paradigms of the legacy projects. This is why software architectures should add testability as a requirement for their development cycles.

    It might be said that an application can be deemed easy to be tested if their components (assemblies and classes) are easy to be tested.


    An easy-to-be-tested class is one which can be easily isolated from the other classes it interacts to. That is, we should be able to test a class individually, without having to be concerned with other classes' implementation. Thus, if a unit test fails, it would be much easier to find the source of the bug.

    In unit testing, we isolate the class being tested by creating mocks of the classes it depends on. Mocks are fake instances of a class/interface, and stand for concrete objects. Mocks are critical tools for unit testing isolation.

    This is called "tight coupling", and is a bad design, which hinders the testability of your application, because you will not be able to easily isolate the two classes. Most mock frameworks can't work with tight-coupled classes. Certainly there are tools (like TypeMock) which can do that job.
    Dependency Injection Pattern, also known as Inversion of Control (IoC)

    The Dependency Injection Pattern (a.k.a Inversion of Control)

    InvoiceServices.Test project

    /*
    * Created By HOME
    * User: BPLOVEGCY
    * Date: 2007-6-8
    * Time: 16:12
    *
    *
    http://bplovegcy.blogspot.com/
    *
    */

    using NMock2;
    using NUnit.Framework;
    using System;
    using System.Collections.Generic;
    using System.Text;

    namespace InvoiceServices.Test
    {
    [TestFixture]
    public class InvoiceServicesTest
    {
    [Test]
    public void CalculateInvoiceTest()
    {
    Mockery mockery
    = new Mockery();
    ITaxServices mockTaxServices
    = mockery.NewMock<ITaxServices>();

    InvoiceServices invoiceServices
    = new InvoiceServices(mockTaxServices);

    Expect.Once.On(mockTaxServices).
    Method(
    "CalculateTax").With(100.00F).
    Will(Return.Value(
    5.35F));
    float totalAmount
    = invoiceServices.CalculateInvoice(10001, 10002, 10003);

    //we expect an invoice with product codes 10001, 10002 and 10003
    //to have an amount of $100.00 + a tax amount of $5.35 = $105.35
    float expectedTotalAmount = 105.35F;
    Assert.AreEqual(expectedTotalAmount,
    totalAmount,
    string.Format("Total amount should be {0}",
    expectedTotalAmount.ToString()));
    }
    }
    }

    InvoiceServices project
    /*
    * Created By HOME
    * User: BPLOVEGCY
    * Date: 2007-6-8
    * Time: 16:12
    *
    *
    http://bplovegcy.blogspot.com/
    *
    */
    using System;
    using System.Collections.Generic;

    namespace InvoiceServices
    {
    public class InvoiceServices
    {
    ITaxServices taxServices ;

    public InvoiceServices(ITaxServices tax)
    {
    taxServices
    = tax;
    }

    public float CalculateInvoice(params int[] productCodes)
    {
    float invoiceAmount = 100;
    //Here goes the code to calculate
    //the invoice amount based on products
    return invoiceAmount + taxServices.CalculateTax(invoiceAmount);
    }
    }

    public class TaxServices:ITaxServices
    {
    public TaxServices() { }

    public float CalculateTax(float invoiceAmount)
    {
    float tax = 0;
    // Here goes the code to calculate invoice tax
    return tax;
    }
    }

    public interface ITaxServices
    {
    float CalculateTax(float invoiceAmount);
    }
    }


    The Northwind Solution: putting things together

    The application architecture looks like this:


    The Model-View-Presenter pattern

    To solve this problem, there is a set of design patterns such as Model-View-Controller and Model-View-Presenter. I prefer using the MVP approach because it enhances testability.
    Model: The domain model objects. (e.g. the Customer class)

    The participants of a MVP pattern are:
  • Model: The domain model objects. (e.g. the Customer class)
  • View: A lightweight user interface. (e.g. Windows form)
  • Presenter: A layer coordinationg interaction between the view and the model.

  • Martin Fowler describes the view and presenter components of MVP as Passive View and Supervising Controller. This means that, instead of simply calling the presenter, the view hands off its events to the presenter. Then the view becomes passive, because now the presenter is the responsible for coordinating all presentation logic. Making the view as simple as possible enables us to create automated unit tests on the presentation layer. In addition, there will be very little risk of non-tested view functionality.

    Separated Interface pattern

    Taking a closer look on the interaction between view and presenter in the package diagram below, we'll see that the Northwind sample uses a Separated Interface pattern. The Windows Form named CustomerView implements an ICustomerView interface, which is placed in another assembly (the presenter package). The CustomerPresenter object knows it will have to control a view, but since it holds a reference to the ICustomerView interface, it really doesn't know how the concrete view is going to look like. This pattern is another good design for enabling unit testing, because we'll be able to easily inject a "mocked" view in the presenter, thus performing isolated unit tests on presentation logic.


    Widgets

    The Services layer

    Instead, it should call methods in the Services layer. The Services layer is a good design option, because it exposes well defined processes of the application. Besides, it is a good place where you can define transaction scope, and if you want to implement transactional updates on domain objects, you can implement the Command pattern in Service layer (the Command pattern would enable you to support undoable operations on domain model objects).

    Services layer test results


    The Tranfer Objects layer

    The transfer objects are read-only POCOs (Plain Old C Sharp Objects) that transfer domain model object data between the layers in the application.

    Tranfer Objects layer test results


  • The ActiveRecord attribute provides the table name the domain object is mapped to. This attribute is used by the ActiveRecord framework, an Object-Relational Mapper framework, and will be discussed later on in this article.
  • The MappedTO attribute provides the TransferObject fullname, and is used by the TOHelper class (in the TransferObjects package) in the Domain Object-to-Transfer Object copy operations.

  • The Unit of Work pattern


    The Active Record pattern

    Martin Fowler describes Active Record as

    An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.

    The Castle ActiveRecord was built on top of NHibernate, and provides a powerful, yet simple, set of Object-Relational Mapping functionalities which allows you to persist your domain objects very easily. With Castle ActiveRecord, you can focus on designing your domain model, while saving time working in data access maintainance.

    The Repository pattern

    The last pattern in this article is the Repository pattern. Edward Hieatt and Rob Mee state that the Repository pattern

    Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects








    No comments: