Mocking a DataContext with TypeMock

| | Comments (1) | TrackBacks (0)

A couple of weeks ago, I blogged about a problem I was having that took more time to solve then expected; it was a task that I thought would be fast but turned out not to be. I jokingly described it as the sort that costs you time you would have otherwise spent on a coffee break. Well, unfortunately, I ran into another such dilemma this week, so I thought I would collect the descriptions of these time sinks into an ongoing series which I'll call Coffee Breaks. In it, I'll present the time consuming problems that I bump into, and how I solved them. To kick it off, I give you the second in this new series:

This week, I needed to write a unit test that would isolate a LINQ to SQL DataContext, allowing me to call the methods that mapped my stored procedures and return known data instead of information in a database. This would allow me to test my data access logic without having to establish a connection to a back-end server. To separate my DataContext, I wanted to use TypeMock Isolator 5.1. This seemly simple problem ended up taking a while to solve because A) I'm new to TypeMock Isolator and wasn't familiar with its API, and B) because the SPROC wrappers returned an ISingleResult<T> which I couldn't simply replace with an array or something.

Learning a new library just takes time. While learning Typemock's and applying it to this problem, however, I ran into a couple of obstacles that protracted the effort and eventually cost me my coffee break (so to speak). The issues where these:

  • In order to substitute my known list of results for those returned by the SPROC wrapper method, I had to create a dynamic mock and figure out how to inject my data into it.
  • If I tried to swap in an array when GetEnumerable was called on the dynamic mock, I kept getting a StackOverflowException when executed inside Resharper's test runner.

To elaborate a bit, let me begin by showing the DataContext definition that was generated when I drug the stored procedure from the Server Explorer to the ORM designer:

    1 public partial class MyGoodDataContext : DataContext

    2 {

    3     [Function(Name = "dbo.MyGoodStoredProcedure")]

    4     public ISingleResult<MyGoodStoredProcedureResult> MyGoodStoredProcedure()

    5     {

    6         IExecuteResult result = this.ExecuteMethodCall(this,

    7             ((MethodInfo)(MethodInfo.GetCurrentMethod())));

    8         return ((ISingleResult<MyGoodStoredProcedureResult>)(result.ReturnValue));

    9     }

   10 }

Given this, I had to figure out a way to replace ISingleResult<T> with some object that contained my test data. The only concrete implementation of ISingleResult<T> in the .NET framework is internal, so I tried to create a fake one and replace the original one with my test double using TypeMock Isolator's SwapNextInstance method like this:

var fake = Isolate.Fake.Instance<ISingleResult<MyGoodStoredProcedureResult>>();

            Isolate.SwapNextInstance<ISingleResult<MyGoodStoredProcedureResult>>().With(fake);

This produced a run-time error telling me to use the MockObject method on the MockManager class to create a dynamic mock instead (perfect error message BTW!).

After some fiddling, I figured out this class's API, and the necessity to initialize it via attributes or by an explicate call to its Init method. Afterward, I was able to create a dynamic mock object of type IResultSet<T>. Now I had to somehow cause my canned info to be returned when this object was enumerated. To this end, I replaced the return value of the mock object's GetEnumerable method with my data like this:

results.ExpectAndReturn("GetEnumerator", expected.GetEnumerator(), 1);

At first, the variable "expected" was an anonymous array of MyGoodStoredProcedureResult objects. However, every time that I enumerated this collection in the code under test, a StackOverflowException was through and my test runner would crash. I replaced it with a object of type List, and it worked (bug I reckon).

With those issues resolved, I was able to fake my DataContext and isolate it from the database using this unit test:

    1 [Test, VerifyMocks, Isolated]

    2 public void Test()

    3 {

    4     // Arrange

    5     var dataContext = Isolate.Fake.Instance<MyGoodDataContext>(Members.ReturnRecursiveFakes);

    6     var results = MockManager.MockObject<ISingleResult<MyGoodStoredProcedureResult>>();

    7     var expected = GetExpectedResults();

    8 

    9     results.ExpectAndReturn("GetEnumerator", expected.GetEnumerator(), 1);

   10     Isolate.SwapNextInstance<MyGoodDataContext>().With(dataContext);

   11     Isolate.WhenCalled(() => dataContext.MyGoodStoredProcedure()).WillReturn(results.Object);

   12 

   13     // Act

   14     var actual = MyGoodRepository.MethodThatInTurnCallsMyGoodStoredProcedure();

   15 

   16     // Assert

   17     for (var i = 0; i < actual.Count; i++)

   18         Assert.That(actual[i], Is.EqualTo(expected[i]));

   19 }


Problem solved, but not before coffee time was over :-(