Introduction
A bit of background first. Quoting Wikipedia, “In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A programmer typically creates a mock object to test the behavior of some other objects”. It is one important technique used in test-driven development. However, in order to use this technique, objects have to be “mockable”. In C#, it usually means instance objects with interfaces or abstraction, such as used in NMock. Static objects and static methods are generally not mockable.
With the help of PostSharp, mocking anything that has a value becomes possible, that includes methods that return a value, and properties no matter instance or static. Even though object itself are not literally “mocked”, it is equivalent to being mocked if all or some of its properties and methods are mocked.
For those who are not very familiar with PostSharp, it is a tool that can intercept any usage of any method or property. New values can be returned without actually invoking the intercepted method or property. There are more features PostSharp provides, this is the link to their website, http://www.sharpcrafters.com/.
Using the code
To use the small framework included with the article, following is the steps to do for a typical test:
- Attach a MockMethod attribute to a target method, attach a MockProperty to a target property.
- Set up TestApparatus by adding conditions of interception and return values if intercepted.
- Make a test that involves those mocked methods and properties, and run
Following is a very simple test
1 public class StaticProvider
2 {
3 [MockMethod]
4 public static bool Provide(int input)
5 {
6 throw new NotImplementedException("Static Provider Not Implemented");
7 }
Line 3 marks the Provide method on Line 4 to be a mockable method.
1 [TestMethod]
2 [Description("With both methods mocked, the test should pass")]
3 public void AllSuccess()
4 {
5 TestApparatus.MockStatic<bool, int>(StaticProvider.Provide).Any().Return(true);
6 TestApparatus.MockInstance<InstancedProvider, bool, int>(i => i.Provide).Expecting(1).If(a1 => a1 >= 0).Return(true);
7 SampleObject sample = new SampleObject();
8 sample.Methods();
9 }
Line 5 tells the framework to intercept any call to static method StaticProvider.Provide(int) and return true then.
Line 6 let the framework to intercept the instance method InstancedProvider.Provide(int) when the input value is great than 0 and return true then. It also tells the framework to expect only one call during the test.
Following examples shows how to mock properties.
1 [TestMethod]
2 [Description("Mocking a specific property")]
3 public void MockingProperty()
4 {
5 const int mocked = 12034;
6 TestApparatus.MockProperty(() => StaticProvider.Value).Any().Return(mocked);
7 TestApparatus.MockProperty((InstancedProvider i) => i.Value).Expecting(1).Any().Return(mocked);
8 SampleObject sample = new SampleObject();
9 Assert.AreEqual(mocked, sample.Values());
10 }
Line 6 mocks the static property StaticProvider.Value to return the mocked value at any time.
Line 7 mocks the instance property InstancedProvider.Value to run the mocked value at any time.
What happened in side the framework is quite straight forward. All mocked methods and mocked properties got internally intercepted, but only those ones that fit the conditions specified are being overridden. The framework did nothing more than recording the requirements of an interception and carrying it out during runtime.
The project is done using Visual Studio 2008 with .Net 3.5. It has been verified that the project works under Visual Studio 2012 too without any change. PostSharp must be installed in order and reference to PostSharp library may need to be refreshed.
Points of Interest
This method of mocking, as built upon PostSharp, requires source codes for methods and properties to be mocked. And the code is modified transparently by PostSharp during compilation. That is, the binaries are not same as the binaries complied without PostSharp. The differences can be revealed by Reflector or ILDASM. The good news is, thanks to PostSharp, the altered binaries run the same as the unaltered versions, and the changed logic are hidden from the Debugger. Then, naturally, one will ask if how to mock a system method without source, such as int.Parse (). One solution is to write a wrapper method around the system method and make the new method mocked. Redirect all call to the original method to the new method. Also setup tests around the new method rather than the original one. Example is shown below.
1 [MockMethod]
2 public int Parse(string s)
3 {
4 return int.Parse(s);
5 }
6 [TestMethod]
7 public void Test()
8 {
9 TestApparatus.MockInstance<Tests, int, string>(t => t.Parse).Any().Return(0);
10 }
Another problem for this altered-binary approach is that the binaries being tested are not the same ones to be deployed. They are equivalent to each other, as guaranteed by PostSharp. Thus the concern is more or less about not to deploy the test version into production environment. In the presented framework, MockMethodAttribute and MockPropertyAttribute contain conditional statements to change themselves into simple attributes for non-debug builds. PostSharp will not recognize those simple attributes, thus leaving the production binary unaltered. This helps in separating testing binaries against production binaries.
In short, comparing to interface-based mock test framework, such as NMock and Rhino Mock, this approach has the freedom to mock anything static. No interface is needed for testing purpose, which is a relief for code that is not designed for mock test in the beginning. Comparing to profiler-based mock test framework, such as JustMock, this approach has an advantage in performance since only attributed methods and properties will trigger an interception.
Final Words
This posted example is only a small demo-purpose project to show how to do mock. But for anyone interested, it contains enough information to become a full scale testing framework. Some useful additions may include: interception of object creation, execution of custom logic upon interception instead of returning simple value.
Thanks for reading.