Thursday, October 06, 2005 - Posts

Mock objects: Sealed doesn’t suck as much as it used to.

Back in 2003 Chris Sells wrote a blog post called ”Sealed Sucks” on the pros and cons of sealing a class. When I recently wrote some tests for an OR-mapping framework, I shared Chris’ feelings on the sealed keyword. The reason was simple, I had a class which mapped objects to stored procedures that exposed a single method to persist objects, and I had to ensure that the correct procedure was called, the correct parameters where passed to the procedure and so forth. The OR-mapper’s constructor expected a SqlConnection object as its parameter, so I figured that I’d just pass it a mocked SqlConnection.

NMock has been my tool of choice for mocking objects, but since SqlConnection is a sealed class you’ll get an exception if you try to mock it using NMock. Writing a mock SqlConnection by hand is neither an option, because you won’t be able to cast it to a SqlConnection.
Luckily, I came across a nifty mocking framework called TypeMock.NET which has the ability to mock any member of any concrete class, no matter whether the members are non-virtual or private, or the class is sealed. TypeMock.NET uses AOP-like techniques at the IL level to intercept usages of any type and member. It does this by using the .NET Framework’s profiler API to monitor execution. When the CLR loads a method, TypeMock.NET replaces the IL with calls to the mocked code.

Below is an example of how to assert that an a SqlParameter with the appropriate customer id is added to a SqlCommand’s Parameters collection when a Customer object is persisted.
[TestFixture]
public class DataServiceTest
{
            [SetUp]
            public void Setup()
            {
                        MockManager.Init();
            }
            [Test]
            public void TestDataService()
            {
                        Customer customer=new Customer();
                        customer.Id="42";
                        string connectionString="Mock Connection String";
                        Mock mockSqlConnection=MockManager.Mock(typeof(SqlConnection));
                        mockSqlConnection.ExpectConstructor().Args(connectionString);
                        Mock mockSqlCommand=MockManager.Mock(typeof(SqlCommand));
                        Mock mockSqlParameterCollection=MockManager.Mock(typeof(SqlParameterCollection));
                        mockSqlParameterCollection.ExpectAndReturn("Add",1).Args("CustomerID",customer.Id);
                        mockSqlCommand.ExpectCall("ExecuteNonQuery",1);
                        mockSqlConnection.ExpectAndReturn("CreateCommand",new SqlCommand());
                        DataService dataService=new DataService(new SqlConnection(connectionString));
                        dataService.SaveObject(customer);
            }
            [TearDown]
            public void TearDown()
            {
                        MockManager.Verify();          
            }

}

There are a couple of things to take note of in the above example. When I register mock objects for the SqlConnection, SqlCommand and SqlParameterCollection types, the actual mock objects aren’t created until I instantiate a new instance of either of the classes. This is the reason why I return an actual SqlCommand instead of a mock object in my mock implementation of SqlConnection.CreateCommand. Further, I do not need to manually create an instance of the SqlParameterCollection, because this class will automatically be mocked when the SqlCommand creates is Parameters collection.

Finally, I have to call the MockManger.Verify method to assert that all of my expectations have been fulfilled. This is akin to using Assert statements in a regular NUnit test.

Thanks to TypeMock.NET sealed doesn’t suck as much as it used to.