Mocking LINQ Queries, Extension methods and Anonymous Types
One of the things I wanted to show at my Interactive session on unit testing tips and tricks at TechEd this year was how you can "Stub" out results from LINQ Queries, or mock\stub Extension methods in .NET 3.5 (what's the difference between mocks and stubs?)
The only mocking framework in existence that can truly stub out LINQ queries and extension methods is TypeMock.NET. Another option, if you use LINQ to SQL, is to be able to mock out the DataContext and the IOrderedQueriable interface, as outlined here, to return a custom query result set. I'll talk about the first option in this post, since it is much easier to read and understand. I may touch on the second one in a later post.
TypeMock, I've talked about it before, is a very powerful mocking framework. much more so than Rhino Mocks or NMock, because it allows isolating static methods, private methods, constructors, and basically anything you can do in IL, because it uses the .NET Profiler APIs to intercept method calls and do whatever it wants with them. In that regard, it is almost too powerful because it rids of of the need to actually design your code for testability, and just test it as is (how you actually write the tests is another matter for another post).
But I'd rather have the argument whether TypeMock is good or bad, than not have TypeMock on the scene at all, simply because it's just too damn valuable as is (disclosure: TypeMock is owned by a friend of mine).
It's the only real framework that can deal with real untestable legacy code and still allow you to isolate classes enough to test them without changing the code under test itself.
It's the only framework that allows to write unit tests for code that is based on .NET 3.5 and uses LINQ Queries and extension methods, allowing to isolate these two types of code and thus be able to separate things from their dependencies.
Here are some examples I wanted to show (takes fro the examples that come with the TypeMock download:
Example: Stubbing out LINQ Query Results
First, we're going to create one fake result to return (array) from the LINQ query, and also introduce the real data source from qhich to query (realCustomerList). note that the customer list has 3 entries, and the fake list has only two entries.
1: ///Two Customer instances used as fake values
2: private static Customer fakeCustomer1 = new Customer { Id = 0, Name = "Fake1", City = "SF" };
3: private static Customer fakeCustomer2 = new Customer { Id = 1, Name = "Fake2", City = "Redmond" };
4:
5: /// A fake list used as return value in the tests
6: private List<Customer> fakeCustomers
7: = new List<Customer> {fakeCustomer1,fakeCustomer2 };
8:
9: /// A list containing 3 cusotmer use as the "real data"
10: private List<Customer> realCustomerList = new List<Customer> {
11: new Customer{ Id = 1, Name="Dave", City="Sarasota" },
12: new Customer{ Id = 2, Name="John", City="Tampa" },
13: new Customer{ Id = 3, Name="Abe", City="Miami" }
14: };
Here's how the unit test looks:
1: [TestMethod]
2: public void MockSimpleQuery()
3: {
4: using (RecordExpectations r = new RecordExpectations())
5: {
6: var answer = from c in realCustomerList select c;
7: r.Return(fakeCustomers);
8: }
9:
10: var actual = from c in realCustomerList select c;
11:
12: Assert.AreEqual(2, actual.Count<Customer>());
13: Assert.IsTrue(actual.Contains(fakeCustomer1));
14: Assert.IsTrue(actual.Contains(fakeCustomer2));
15: }
Line 6 is the one that tells the TypeMock record what LINQ query to intercept.
Line 7 tells TypeMock what value to return when this query is intercepted. that means the query will never really take place.
finally, we assert that what we got from the query is the fake list of customers. Pretty darn easy.
But is this a good test? Not really. this is only a demo of what you code intercept with typeMock. a real unit test will actually test a piece of code that runs this query, and, instead of providing it with a list of objects to query, will just provide it with the fake return value to receive
Example: Test that an anonymous type is created correctly
1: [TestMethod]
2: [VerifyMocks]
3: public void MockAnonymousTypeTest()
4: {
5: using (RecordExpectations rec = new RecordExpectations())
6: {
7: //Mock the creation of the Anonymous Type
8: var p1 = new { Name = "A", Price = 3 };
9: rec.CheckArguments();
10:
11: //fake the value of the Name Property
12: rec.ExpectAndReturn(p1.Name, "John");
13: }
14: //if creation will be done diffrently an exception will be thrown.
15: var target = new { Name = "B", Price = 3 };
16: //verify that the fake value is returned
17: Assert.AreEqual("John", target.Name);
18: }
Line 8 tells the typemock recorder that this is the anonymous type creation we'd like to intercept.
Line 9 tell it to assert internally that the anonymous type is indeed created with the correct "Name" property value and the correct "Price" property value. so if I later initialize the anonymous type with the wrong values, I will get a test exception based on the expected values. Pretty neat.
Line 12 is just an example of how you can also intercept and retun whatever value you'd like from the property of an anonymous type. in this case the name will always return "John" even though it may have been initialized differently.
Example: Mocking Extension Methods
Extension methods are static methods, which means you can't replace them with a different method instance. there are ways to design yoru code so that the calls to the static methods are testable, but assuming you're testing code that cannot be changed, or that is hard to redesign, intercepting the method call itself is almost the only choice.
assume you've extended the Point class by adding this extension method:
1: public static class Extend
2: {
3: public static Point Multiply(this Point extendedInstance, int scalar)
4: {
5: extendedInstance.X *= scalar;
6: extendedInstance.Y *= scalar;
7: return extendedInstance;
8: }
9:
10: }
Now you can write a test like this:
1: [TestMethod]
2: [VerifyMocks]
3: public void MockExtensionMethod()
4: {
5: using (RecordExpectations rec = new RecordExpectations())
6: {
7: Point mocked = new Point(7, 9);
8: mocked.Multiply(6);
9: rec.Return(new Point(5, 5));
10: }
11:
12: Point target = new Point(7, 9);
13: Point actual = target.Multiply(6);
14:
15: //Verify the returned vcalues
16: Assert.AreEqual(5, actual.X);
17: Assert.AreEqual(5, actual.Y);
18: }
in line 7 we're actually using the extension method as part of our recorder, and telling the record to return a specific value. then we later use that value in our code under test (like 13) and assert that we god the mocked value instead of the real one (the extension method is not executed).
What about other technologies?
could you use this to test code that relies on other non testable technologies, like windows workflow, WCF etc? the answer is a big yes. the same principles apply, and most of these frameworks were not built with testability in mind.
it's in those cases where people ask "how can I test such code where everything is sealed, private and static?" that I tell them that, currently, TypeMock is the only framework that solves this problem at a satisfactory result.