Testing Abstract Classes
The first and foremost use of Partial Mocks can be with testing abstract classes. It allows the mocked implementation of abstract members which is used while testing other members of the type under test. Let's introduce an abstract class and see how it is conventionally tested. Then we will see how we can improve the testing experience with the introduction of partial mocks.
Let's create Visual C# class library project DrawingApp and add an abstract class, named Point, to the project. The definition of Point might be as follows:
A point has some position in space which is specified by CurrentLocation property. It can be moved to a new position using MoveTo() method. There might be some restrictions in moving the point to a new location. This can be taken care of by the specialized Point (derived class) using IsMoveAllowed() method. Here Coordinate is a type to hold Cartesian Coordinates of a point. We can define it as follows:
In order to test Point, let's introduce a MSTest project DrawingAppTest. Now we want to test MoveTo() method of Point. But the issue is that Point is an abstract type. Since abstract types can not be instantiated, we can not test MoveTo() method directly. The natural solution seems to be inheriting from Point and provide a dummy implementation of IsMoveAllowed() which always return true. This would make sure that MoveTo() is always called for testing purpose.
Before doing that we need to make sure that internals from DrawingApp are visible to DrawingAppTest. We can do this by updating AssemblyInfo.cs with the following:
Since TestablePoint is a subclass of Point so it would be using the same definition of MoveTo() as the base class here. We can simply use AAA [Arrange, Act, Assert] for testing MoveTo() as follows:
Here we are setting an arbitrary position of a point of the type TestablePoint. We are moving the location of the point by calling MoveTo() with a new Coordinate. Then we are verifying if the position has been successfully updated by checking the X and Y coordinates of current location. Let's run this if the test run is successful.
The test run is successful. We should be the happiest developers in the world. But wait! What if we have many abstract classes in our library. Using this approach would push us in inheriting all those types providing the definitions of all the abstract members. This doesn't seem right. Can this be improved? Hell Yes!!!
This situation can be improved by using Partial Mocks. Partial Mocks allow us to provide a dummy implementation of some members of a type so that other members might be tested. Rhino Mocks also supports partial mocks. We can set expectations on these mocks and specify what should be returned if the mocked method is called. RhinoMocks support creation of partial mocks using MockRepository.GeneratePartialMock
As you can see that we are setting the expectation on pointMock to always return true. Then we are calling MoveTo() method. Finally, we are asserting that the point's location has been updated successfully. Partial Mock also supports verification that the method was called during the act. We can run the test again to verify if the test is successful.
We always need to set expectations on the abstract members, otherwise, this would result in failure when test is being executed. Here we commented out the expectation in the above test and see the result. If you run the test in Visual Studio then it would just show that that test is failed. You would need to debug the test if the test is being run in Visual Studio.
Testing Internal Types with Partial Mocks
There is a small change you would need to do in the above testing if the types are themselves internal. Let's update Point and Coordinate as internal and run the test again. You would see the following error:
Basically RhinoMocks create a dynamic proxy of the type providing the definitions of methods as provided in the expectation. For creating proxy, it uses other assemblies. There is a known fix for this issue. We just need to make sure that the internal types are visible to the assembly used by RhinoMock for generation of proxy.
Now run the test again. It should be successful.
Testing Non-Astract classes with partial Mocks
We might want to test non-abstract and concrete types with Partial Mocks. Here we specify mocked implementation of some members of the type and test other members. The members are generally mocked to keep the focus of test on a single method (atomic). We can do it to make the test more deterministic and run faster. In order to understand this, let's build on top of previous example. Here we are introducing a concrete point providing implementation of abstract member [IsMoveAllowed] of Point.
Here we needed to shadow MoveTo() member as we needed to notify to the service that the point is moved so that other users get notified about this move. Just assume that we just had this option to be calling NotifyMoveToService() when MoveTo() gets called. We are also adding a virtual member SaveToMemory() which is not used by MoveTo(). Now we need to test MoveTo() method for ConcretePoint type. Let's add the following test:
This is nearly like the previous test. Here we are testing that CurrentLocation gets updated when we call MoveTo() with new coordinates. We are creating expectations for the mocked object providing mocked implementation of NotifyMoveToService() as we don't want to hit the service every time we are running the unit test. We are also checking that IsMoveAllowed() and NotifyMoveToService() methods are called during test run making sure that SaveToMemory() hasn't been involved. Now let's run the test and get surprised!
So this is a failed test. But what is wrong??? In order to find that out we need to see the test result by right clicking the test and select "View Test Result Details".
It shows the following details:
When we check which particular it refers to be having issues then we find the following:
Hmm, so we cannot provide mocked implementation of non-virtual members. Basically to create partial mocks, RhinoMocks generates a dynamic proxy of the type under test. It then overrides virtual members on which the expectations are being set. Since non-virtual members can not be overridden, that is why, we have this issue. Let's change the definition of NotMoveToService() and make it virtual.
Now we run the test again and see if we are successful.
Happy Unit Testing!!!