Faking API for stubs and mocks

Just for fun I started to build my own API for taking care of stubbing and mocking while writing my tests. It wasn’t all for fun, since I didn’t like the API of the Moq-framework (read earlier writings about this) I thought that I could wrap Moq in my own API. Since I’m not consuming all the functionality of the Moq-framework, I will get a more slim API that doesn’t tangle stubs and mocks. It will also make it possible for me to switch (why I would like this I really don’t know) for another framework than Moq; or actually build my own instead of just a wrapper.

The result? Well as I see it it all start with the intent of faking something (use a stand-in); either as a simple stub or a mock. And the stub and mock should have different configuration API.

The result so far (since I add functionality when I need it)

var newUserAccount = UserAccountFactoryForTests.CreateValidUserAccount();
var entityStoreMock = Fake<IEntityStore>.Mock();
var componentContainerStub = Fake<IoCContainer>.Stub()
    .Returns(c => c.Resolve<IEntityStore>(), entityStoreMock.MockedObject);
var service = new SecurityService { ServicesIoCContainer = componentContainerStub.StubbedObject };

The code can be downloaded from here.

//Daniel

The Moq, mockingframework, and the tangling of Stubs and Mocks

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