Wednesday, August 13, 2008

Unit Testing using Mocks - FillWithMocks, Fill all or only selected properties with Mocks using only a single method call

I have been doing TDD for about two years now and using mock testing for interaction based unit testing in my projects. What I have learned over this time is, a unit testable design leads to introduction of interfaces and dependency injection for testing a code in isolation. And when I want to perform tests on my interactions, I need to create mock objects and inject these mock instances to my object under test. Sometimes, a unit test class needs to create quite a few of such mock objects and I feel this can be done using a simple wrapper around the usual mocking frameworks.

I suggest a similar and even more powerful wrapper so that you don't need to create instances for each of the mock objects, rather do it in a single call for all your desired mocks. I have shown this method for NMock2, however, its evident that you can write your own method for your favorite mocking framework just using this code as a reference.

This code is written in C# 3.0 and should compile in .Net 3.5. You will need to add a reference to NMock2 to compile this. Also, you need to know a bit about Reflection to understand the following code fragment.

Disclaimer: This code is just a simple example and it may not suit all your needs.

1 using System;

2 using System.Collections.Generic;

3 using System.Linq;

4 using System.Text;

5 using System.Reflection;

6

7

8 namespace NMock2.Extensions

9 {

10 public static class MockeryExtensions

11 {

12 /// <summary>

13 /// This method will fill all properties of the 'target' of interface type having a setter

14 /// </summary>

15 /// <param name="mocks"></param>

16 /// <param name="target"></param>

17 public static void FillWithMocks(this Mockery mocks, object target)

18 {

19 Type targetType = target.GetType();

20 PropertyInfo []properties = target.GetType().GetProperties();

21

22 foreach (PropertyInfo property in properties)

23 {

24 Type ptype = property.PropertyType;

25 if (ptype.IsInterface)

26 {

27 if(targetType.GetMethod(string.Format("set_{0}", property.Name)) != null)

28 {

29 targetType.GetProperty(property.Name).SetValue(target, mocks.NewMock(ptype), new object[] { });

30 }

31 }

32 }

33 }

34

35 /// <summary>

36 /// This method will fill the properties with names specified in the propertyNames array having a setter

37 /// </summary>

38 /// <param name="mocks"></param>

39 /// <param name="target"></param>

40 /// <param name="propertyNames"></param>

41 public static void FillWithMocks(this Mockery mocks, object target, params string[] propertyNames)

42 {

43 Type targetType = target.GetType();

44 foreach (string propertyName in propertyNames)

45 {

46 PropertyInfo pInfo = targetType.GetProperty(propertyName);

47 if (pInfo != null && targetType.GetMethod(string.Format("set_{0}", propertyName)) != null)

48 {

49 pInfo.SetValue(target, mocks.NewMock(pInfo.PropertyType), null);

50 }

51 else

52 {

53 throw new ArgumentException(string.Format("the property {0} is not found or does not have a setter", pInfo.Name));

54 }

55 }

56 }

57 }

58 }

So, with this extension method in place, you can go and write your test code in a much compact manner. The following is an example showing one possible use of this method in your NUnit test code.

83 public interface IMyInterface

84 {

85 void MyMethod(string name);

86 }

87

88 public class MyExampleClass

89 {

90 public IMyInterface MyPropertyOne

91 {

92 get;

93 set;

94 }

95

96 public IMyInterface MyPropertyTwo

97 {

98 get;

99 set;

100 }

101

102 public IMyInterface MyPropertyThree

103 {

104 get;

105 set;

106 }

107

108 public MyExampleClass()

109 {

110

111 }

112 }

113

114 [TestFixture]

115 public class MyExampleClassTest

116 {

117 private MyExampleClass _myExampleClass;

118 private Mockery _mocks;

119

120 [SetUp]

121 public void Init()

122 {

123 _myExampleClass = new MyExampleClass();

124 _mocks = new Mockery();

125

126 //This call fills all the three properties with mocks

127 _mocks.FillWithMocks(_myExampleClass);

128

129 //This call fills only MyPropertyOne and MyPropertyTwo with mocks

130 _mocks.FillWithMocks(_myExampleClass, "MyPropertyOne", "MyPropertyTwo");

131 }

132 }

Comments are welcome!