Spencer Prahl

.Net Developer & Architect

  Home  ::  Contact  ::  Syndication  ::  Login
  9 Posts  ::  3 Stories  ::  8 Comments  ::  5 Trackbacks  
Mock Unit Testing with Dependency Injection

Overview
In software development, unit testing is performed to validate a small module or portion of functionality at a fundamental level. Test cases are written at the function or procedure level to validate inputs and outputs. Several test cases can be written to validate a given piece of functionality and then, when new functionality is added or changed, the existing test cases can be used to validate that existing functionality has not been affected.
 
In theory, unit testing sounds like an effective tool that can be used to improve application quality. The improved quality comes at a price however, because developers must take into account the additional effort required to develop useful and relevant test cases while still meeting deadlines.  To complicate matters, many application components rely upon other components and domain state to function properly, increasing the test effort.  An application component may require a database, a web service, or even the current outside temperature in order to work properly.

One of the arguments against the use of unit testing is that failures that arise from interactions between components are not caught.  Unit tests are not meant to evaluate the “big picture”. They are merely a tool to enhance the overall quality of a product that is created as a result of combining several small components. Integration and system testing should not be sacrificed just because a complete set of unit tests exists.

The long-term benefits of a comprehensive set of unit tests outweigh short-term costs in a couple of ways. First, when code is written with test cases in mind, the code itself is much simpler and the developer is forced to adhere more closely to good programming practices such as encapsulation. Next, if a set of test cases exists for a component, the developer can make changes or re-factor the component and feel confident that the change did not break any existing functionality. In addition, having unit tests for a component allows the developer to test the component without having to run the entire application each time some new piece of functionality requires testing.

Dependency Injection
One of the biggest obstacles to implementing unit tests is that many components rely upon domain state in order to function properly. For instance, a Business Services component may have a dependency upon a Data Access Layer component, and subsequently a database. This dependency makes it difficult to write unit tests for the Business Services component without a database and the corresponding Data Access Layer. In order to avoid this dependency, the Business Services component can make use of the Dependency Injection pattern. (See Martin Fowler's Article for a technical description of the Dependency Injection pattern.)

What It Means
Say a business object is instantiated that performs business logic including calling a data access component to retrieve data from a database. If the business object has internal knowledge of the data access component that it needs and creates that object internally, the business object has “control” of which data access component it is going to use.  In the following example, the ''UserServices'' class has “control” of which data access layer component it is going to use. In this case, the ''UserServices'' instantiates a specific ''UserDAL'' object.

1namespace BusinessServices
2{
3 public class UserServices : MarshalByRefObject
4 {
5  private UserDAL _userDAL;
6  public UserServices ()
7  {
8   _userDAL = new UserDAL();
9  }
10 
11  public object GetUser(int userPK)
12  {
13   object o = _userDal.GetUser(userPK);
14   o.FullName = o.FirstName + " " + o.LastName;
15   return o;
16  }
17 }
18}

By using '''Dependency Injection''', the ''UserServices'' component is no longer responsible for determining which data access layer component it needs. Instead, the consumer of the ''UserServices'' object specifies the correct data access layer to use as shown below.

1namespace BusinessServices
2{
3  public class UserServices : MarshalByRefObject
4  {
5    private IUserDAL _userDAL;
6    public UserServices (IUserDAL dal)
7    {
8      _userDAL = dal;
9    }
10 
11    public object GetUser(int userPK)
12    {
13      object o = _userDal.GetUser(userPK);
14      o.FullName = o.FirstName + " " + o.LastName;
15      return o;
16    }
17 }
18}
19
20

Notice that the constructor for the UserServices class now accepts any object that implements the ''IUserDAL'' interface. This is where '''Dependency Injection''' has taken place. The control of which implementation of the ''IUserDAL'' the ''UserServices'' object is going to use has been placed on the shoulders of the consumer of the ''UserServices'' object. The dependency has been injected using the constructor.

To take the concept a step further, the injection can be controlled via configuration and the factory pattern as shown below.


1namespace BusinessServices
2{
3 public class UserServices : MarshalByRefObject
4 {
5  private UserDAL _userDAL;
6  
7  public UserServices (IUserDAL dal)
8  {
9   string typeName = ConfigurationSettings.AppSettings["IUserDALType"];
10   Type t = Type.GetType(typeName);
11   _userDAL dataAccess = (IUserDAL)Activator.CreateInstance(t);
12  }
13  
14  public object GetUser(int userPK)
15  {
16   object o = _userDal.GetUser(userPK);
17   o.FullName = o.FirstName + " " + o.LastName;
18   return o;
19  }
20 }
21}

The implementation of the ''IUserDAL'' component is controlled via a configuration file entry. Although this method provides the most flexibility, it does have the drawback of requiring entries for each type in the configuration file.

Why Use It?
By moving the control of an object’s dependency to the consumer of the object, additional flexibility is obtained that allows for “plug-and-play” implementations of dependent components. The example above allows any implementation of the ''IUserDAL'' interface to be used within the ''UserServices'' object. This is important because it supports the notion of using a “mocked” implementation. This in turn allows the creation of unit tests that will test a component without relying upon domain state.

Mock Objects
Objects used to mimic behavior of real objects in a controlled way are called “Mock Objects”. Mock objects are generally created to test the behavior of some component that relies upon some external state. An example of a mock object would be the use of a crash test dummy to see what happens in a car accident. In programming, a mock object might be used to simulate domain state that an object being testing requires. A component that tracks motor temperatures in a manufacturing plant, for example, would require temperature readings from an external source. Based upon the received temperature readings, the tracking component might send notifications to an email address or shut down the machine depending upon the temperature reading received. Testing this without mock objects would require connections to the motor and a way to make the motor run hotter or colder depending upon the test case. With mock objects, the motor connection can be simulated for each test case.

Unit Testing With Mock Objects
In order to facilitate unit testing with mock objects, some initial thought must be given to the design of the component being tested. If the object that is to be tested with a unit test requires a database connection, for example, how is the object tested without the database? The solution is to use a mock object. In order to use a mock object, the component being tested must have a way to “unplug” the actual database connection and “plug” in the mock connection. This is done using the '''Dependency Injection''' pattern described above. Mock objects have the same interface as the real objects they mimic, allowing the tested object to remain unaware of whether it is using a real object or a mock object.

There are several mock frameworks available. A mock framework provides the groundwork for using mock objects in unit testing. The framework uses component interfaces and .NET refelection to instantiate objects that replace the actual implementations. A couple of the frameworks available are listed below.

* NMock2 –  An open source mock framework
* TypeMock – Not open source. A free version is available as well as upgrades which require licensing fees.

The following section describes some usage guidelines for NMock2 since it appears to provide an adequate amount of functionality to support mock unit testing.

NMock2
NMock2 is an open source mock object unit testing framework available at SourceForge. There are two versions available, one for each .NET Framework version.

How To Use NMock2
NMock2 is the latest version of the NMock open source mock testing framework. Documentation details can be found at NMock.org. The current version of NMock to be used is 2.0. 

NMock2 Test Case
To illustrate when and how the NMock2 framework should be used, a business service type component will be created that has a dependency on a data access layer component. The data access layer component is responsible for retrieving data from a database and returning it to the business service component. The business service component is the component that is being tested; while the data access layer component is the part this is being “mocked”.

Typically, a business service component is in control of what data access layer it needs to instantiate in order to get the data. A common example is shown below.


1public class BankAccountServices
2{
3 private BankDAL _bankDAL;
4
5 public BankAccountServices()
6 {
7  _bankDAL = new BankDAL();
8 }
9}

Notice that the constructor of the ''BankAccountServices'' object is in full control of which data access layer component it is going to use (''BankDAL''). The ''BankDAL'' would be defined as follows:


1public class BankDAL
2{
3 public BankDAL()
4 {
5 }
6}

If there is a requirement that the ''BankAccountServices'' component provide a method that returns the net of all account balances for a given account holder, the method would typically call the database to get all of the required data, calculate the net balance of all accounts (e.g. savings, checking, money market, etc…). The intent of the mock object is to simulate the database connection somehow. In order to do this, some changes must be made.

First, since the ''BankAccountServices'' object needs an external component, in this case the ''BankDAL'', something must be changed so that the “real” ''BankDAL'' can be replaced with a mock ''BankDAL''. This is done by defining and implementing the ''IBankDAL'' interface in the ''BankDAL'' class. The interface is shown below:


1public interface IBankDAL
2{
3 double GetNetAccountBalances(int accountHolderID);
4}

Now that the ''IBankDAL'' interface is defined, the ''BankDAL'' class must implement the interface. Below is the ''BankDAL'' class with the interface implemented.


1public class BankDAL : IBankDAL
2{
3 public BankDAL()
4 {
5 }
6 
7 public double GetNetAccountBalances(int accountHolderID)
8 {
9  return 0;
10 }
11}

Currently, the ''GetNetAccountBalances()'' method simply returns a zero but it doesn’t really matter what it returns for this example. In reality, this method would query the database for information and return it to the ''BankAccountServices'' object.

The next step is to modify the ''BankAccountServices'' object to use '''Dependency Injection''' which will allow the consuming application to define which implementation of an ''IBankDAL'' object to use.


1public class BankAccountServices
2{
3 private IBankDAL _bankDAL;
4 public BankAccountServices()
5 {
6  _bankDAL = new BankDAL();
7 }
8 
9 public BankAccountServices(IBankDAL dal)
10 {
11  _bankDAL = dal;
12 }
13}

Following the TDD methodology, the next step is to define the test case.

1[TestFixture]
2public class BankAccountServicesTest
3{
4 [Test]
5 public void GetNetAccountBalancesTest()
6 {
7  BankAccountServices svc = new BankAccountServices();
8  double val = svc.GetNetAccountBalances(1);
9  Assert.AreEqual(100, val, "Net account balance is incorrect");
10 }
11}


In order for the test case to compile, the ''GetNetAccountBalances()'' method must be added to the ''BankAccountServices'' class. This method simply serves as a “passthrough” method to get data from the ''BankDAL'' which means that there really is not any critical functionality to be tested. It could be argued that testing this method as it is defined here may be useless because there is no business logic occurring.

1public double GetNetAccountBalances(int accountHolderID)
2{
3 return _bankDAL.GetNetAccountBalances(accountHolderID);
4}
5

If the project is run and the test is executed, it will fail. It fails because it is using the concrete ''BankDAL'' implementation which always returns database data based on the input value. (The input value in the test case is “1” and the return value from the ''BankDAL'' is always “0”. The assumption here is that the ''BankDAL'' looked in a database somewhere for all account information for the account holder with and ID of “1” and returned the net of those account balances.)

So, how is the concrete implementation of the ''BankDAL'' replaced with a mock implementation and how is the return value controlled? First, to inject the mock implementation of the ''BankDAL'', the NMock2 framework must be used. Below is the modified ''GetNetAccountBalancesTest()'' method. Line 4 defines the Mockery object which is part of the NMock2 framework. The “using NMock2;” directive must be added in order use the Mockery object. An ''IBankDAL'' object is also declared. The ''bankDAL'' object variable is then instantiated using the mocks variable and the ''NewMock()'' method of the NMock2 framework as shown on line 7. The framework uses .NET reflection to instantiate the mock implementation of a ''BankDAL'' object.

1[Test]
2public void GetNetAccountBalancesTest()
3{
4 Mockery mocks = new Mockery();
5 IBankDAL bankDAL;
6 
7 bankDAL = (IBankDAL)mocks.NewMock(typeof(IBankDAL));
8 
9 Expect.Once.On(bankDAL).Method("GetNetAccountBalances").With(1).Will(Return.Value((double)100));
10 
11 BankAccountServices svc = new BankAccountServices(bankDAL);
12 double val = svc.GetNetAccountBalances(1);
13 
14 Assert.AreEqual(100, val, "Net account balance is incorrect");
15}
16

Line 9 is an NMock2 call that basically states that when the tested method, in this case ''GetNetAccountBalances()'' is called, the mocked object should expect one call to the mocked ''bankDAL'', of the method ''GetNetAccountBalances()'', with the parameter of 1, and a return value of 100.

 


Feedback

# Mock Unit Testing with Dependency Injection

# re: Mock Unit Testing with Dependency Injection
Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:ComComments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:Comments:ments:Comments:


Post Feedback
Title: 
Name: 
Url: 

Comments: 
Enter the code you see: