Friday, April 17, 2009

NMock and TDD

I’ve always been an advocate for TDD although I think it is an uphill battle. I haven’t given up on having my group adopting TDD but I wish they were using it more and more.

Today I was writing some unit testing in a object model that had many dependencies and I notice the amount of Cra*(#$(. objects (fakes) I had to have in order to test a simple functionality.

Although I use unity to inject these instances it was still a bunch of static classes that didn’t do much. This monolithic approach was hard to change or adapt for different situations.

This situation made me leave my comfort zone and do some research with NMock. I have to tell you… I was hitting my head on my desk thinking why I did not use this before….

So I will spend some time talk about NMock and how you can benefit from using it in conjunction with TDD.

NMock 2.0

NMock is a dynamic mock object library for .NET. Mock objects make it easier to test single components—often single classes—without relying on real implementations of all of the other components. This means we can test just one class, rather than a whole tree of objects, and can pinpoint bugs much more clearly. Mock objects are often used during Test Driven Development.

A dynamic mock object:

  • takes on the interface of another object, allowing it to be substituted for a real one for testing purposes.
  • allows expectations to be defined, specifying how the class under test is expected to interact with the mock.
  • fails the test if any of the expectations are violated.
  • can also act as a stub, allowing the test to specify objects to be returned from mocked methods.

The best way to be Impressed is by Example

The scenario is a PermissionSystem that relies in a authenticated user with certain Permissions to access a resource. You want to test if a specific user can access the resource. The resource can be public, private or semi-private. For simplicity we will only present the following rules and we will only test a simple path:

  • A public resource
      • Anonymous user cannot post
      • Authenticated user can Post
  • Private Resource
      • Anonymous user cannot post
      • Authenticated can only post if it belongs to a role

The code presented is not functional only an example how you can setup expectations in your mocks in order to simulate scenarios.

The IUser interface

   1: interface IUser
   2: {
   3:     long Id {get; set;}
   4:     bool IsAuthenticated {get; set; }
   5: }



The Permission Service Interface





   1: public interface IPermissionService
   2: {
   3:     bool CanPerformTask(IContext context, long cid, string permission, string resource);
   4:     bool IsUserInRole(IRole role, IResource resource);
}





The main service would look like this (not really but .. you got the idea)





   1: public class PostService
   2: {
   3:     public PostService( IUser user, IPermissionService service )
   4:     {
   5:         ...
   6:     }
   7:  
   8:     public void Post( object post, IResource resource )
   9:     {
  10:         if( !user.IsAuthenticated )
  11:         {
  12:             throw new ApplicationException("User cannot post");
  13:         }
  14:         else if( resource.UserAccess == UserAccess.Public )
  15:         {
  16:             Post();
  17:         } 
  18:         else if( resource.UserAccess == UserAccess.Private )
  19:         {
  20:             if( service.IsUserInRole( "Authorized" )
  21:             {
  22:                 Post();
  23:             }
  24:             else
  25:             {
  26:                 throw new ApplicationException("User cannot post");
  27:             }
  28:         }
  29:     }
  30: }






The intent of the test is to verify if the behavior of Post method is correct when a user is authenticated but does not belong to the Authorized role.





   1:  [TestMethod()]
   2:          [ExcpetionException( typeof(ApplicationException))]
   3:          public void TryPostWithAuthenticatedUserUserNotInAuthorized()
   4:          {
   5:              Mockery mocks = new Mockery();
   6:              IUser loggedUser = mocks.NewMock<IUser>();
   7:              IPermissionService permissionService = mocks.NewMock<IPermissionService>();
   8:              IResource resource = mocks.NewMock<IResource>();
   9:   
  10:              Expect.Once.On(loggedUser)
  11:                     .GetProperty("IsAuthenticated")
  12:                      .Will(Return.Value(true));
  13:   
  14:              Expect.Once.On(permissionService)
  15:                  .Method("IsUserInRole")
  16:                  .WithAnyArgument()
  17:                  .Will(Return.Value(false));
  18:   
  19:              Expect.Once.On(resource)
  20:                     .GetProperty("UserAccess")
  21:                      .Will(Return.Value(UserAccess.Private));
  22:              PostService postService = new PostService(loggedUser, permissionService);
  23:              postService.Post(post, resource);
  24:   
  25:          }

  • line 5 we are creating the mock factory if you will. This object will be responsible for creating dynamic proxies for your interfaces.

  • Lines 6,7 and 8 . You are creating dynamic mock objects that mirror your interfaces.

  • The next step is to set expectations. When you code by intent expectations are very clear. For this test you expect the user to be authenticated and also that he is in a specific role called Authorized. In addition you want the resource to be private.



    • On the first example you are saying if the property IsAuthenticated is called in my mock return true.


    • On the second statement you are saying if the Method IsUserRole is called in my mock permission service with any argument return false.


    • And if the resource.UserAccess property is called return private.




Interesting in this construction is that your unit test not only tests the behavior of the method post but also the expectations or how post should relate to dependent objects. When you state Expect.Once.On you are saying that post method should only call this method or property once. If that rule is broken your unit test will fail.



At the end of the test you can still verify if all expectations were met. Ex. If IsUserInRole is not called at all you have a problem. Your test will fail.



Better than that. By changing one expectation you can test a totally different aspect of the Post method. Ex. Make IsUserInRole return true.



Here is a cheat sheet for Nmock that will get you going.



Conclusion



Programmers working with the test-driven development (TDD) method make use of mock objects when writing software. Mock objects meet the interface requirements of, and stand in for, more complex real ones; thus they allow programmers to write and unit-test functionality in one area without actually calling complex underlying or collaborating classes[6]. Using mock objects allows developers to focus their tests on the behavior of the system under test (SUT) without worrying about its dependencies. For example, testing a complex algorithm based on multiple objects being in particular states can be clearly expressed using mock objects in place of real objects.



Apart from complexity issues and the benefits gained from this separation of concerns, there are practical speed issues involved. Developing a realistic piece of software using TDD may easily involve several hundred unit tests. If many of these induce communication with databases, web services and other out-of-process or networked systems, then the suite of unit tests will quickly become too slow to be run regularly. This in turn leads to bad habits and a reluctance by the developer to maintain the basic tenets of TDD.



When mock objects are replaced by real ones then the end-to-end functionality will need further testing. These will be integration tests rather than unit tests.



If before you were able to write tests first and then code; now with Mocks things are much easier. You can start defining your interfaces and quickly you can write tests that reflects the intentions for the class you are testing.



By using mocks the creation process become more organic since you don’t need to have every single implementation of your interfaces in place in order to start testing and coding. You can always isolate the subject of tests and play around with your mocks.

1 comment:

Anonymous said...

Interesting post. Thanks. BTW, some of the source code in the example is not visible.