When I need the powers of a Faking-framework I use the Moq-framework, which I use when I feel that it is to much work for creating a manual Fake (which I only do with stubs). There’s one thing that I really don’t like with Moq, the tangling of Mocks and Stubs. I like to see them as follows:
Fakes
Stubs (is used to make interaction work and to return or throw exceptions etc.)
Mocks (is used to assert against by putting demands/expectations on the interaction)
This is why I wrote “Faking-framework” above. A stub is a fake and a mock is a fake but a mock is not a stub and a stub is not a mock. Glad that we sorted that out!
Don’t get me wrong here, I like the Moq-framework, but what I would like to see in it is a separation of stubs and mocks. The API should let you clearly create a stub or a mock. You shouldn’t be able to turn a stub into a mock. When creating the stub, a class named Mock is confusing and missleading hence should not be used.
Look at the following simple test. I have a shoppingcart for a customer and the cart uses a pricelocator to lookup the prices of the products that I’m adding. The price is determined by looking at the customer, the product and the quantity. In the test I create a stub for my pricelocator, so that I’m sure of the prices it will return. I stub the pricelocator so that the test can query it for two different products for the same customer. Then I add two items to the shoppingcart, which will use the pricelocator when I invoke the GetTotal-function on my shoppingcart.
The test
[TestClass]
public class ShoppingCartTests
{
[TestMethod]
public void GetTotal_TwoValidShoppingCartItems_GivesTotal()
{
const string customerNo = "2010-1";
var item1 = new { CustomerNo = customerNo, ProductNo = "P01-01-00001", Quantity = 2, Result = 101.50M };
var item2 = new { CustomerNo = customerNo, ProductNo = "P01-01-00002", Quantity = 3, Result = 99.75M };
var expectedTotal = item1.Result + item2.Result;
var priceLocatorStub = GetPriceLocatorStub(item1, item2);
var shoppingCart = new ShoppingCart(customerNo) { PriceLocator = priceLocatorStub };
shoppingCart.AddProduct(item1.ProductNo, item1.Quantity);
shoppingCart.AddProduct(item2.ProductNo, item2.Quantity);
var actualTotal = shoppingCart.GetTotal();
Assert.AreEqual(expectedTotal, actualTotal);
}
private static IPriceLocator GetPriceLocatorStub(params object[] items)
{
var priceLocatorStub = new Moq.Mock<IPriceLocator>();
foreach (var item in items)
{
var tmp = TypeCaster.CastAnonymous(item, new { CustomerNo = "", ProductNo = "", Quantity = 0, Result = 0M });
priceLocatorStub
.Setup(pl => pl.LookupPrice(
tmp.CustomerNo,
tmp.ProductNo,
tmp.Quantity)).Returns(tmp.Result);
}
return priceLocatorStub.Object;
}
}
What if I add a expectation to my stub? It is possible, but should it be? Yes it’s possible. With the little change of adding “.AtMost(0)” below, I have created an mock and totally changed the semantics, hence the Assert should change to, but that’s not the point in this blog post. The point is that I think the Moq guys should keep the mocks and stubs API’s sepparated, so when I create a stub I can’t add expectations.
priceLocatorStub
.Setup(pl => pl.LookupPrice(
tmp.CustomerNo,
tmp.ProductNo,
tmp.Quantity)).Returns(tmp.Result).AtMost(0);
The changes I want are:
Instead of:
var stub = new Mock();
I want:
var stub = new Stub();
and not being able to add e.g. AtMost-expectations to the Stub.
Even if this change will not come, I will continue to use the Moq-framework.
To finnish of, I would like to point out that I think this missuse of the terms mocks and mocking is common amongst developers; who gladly uses variablenames like “*Mock” when they actually are creating a stub; who frequently speaks in terms of mocks, when they actually are using stubs.
The complete sourcecode can be downloaded from here.
//Daniel