Sunday, July 14, 2013

Unit testing with Moq

If you're using C# at work and are a little serious about unit testing, then the Moq framework can be your friend. It's simple to use and will do the job for both classical TDDer or a mockist TDDer.

I find this blog although written a while now, still is great as a starting point to understanding and using Moq: http://stephenwalther.com/archive/2008/06/12/tdd-introduction-to-moq

Referencing from the original blog, the following code completes the example so the mockist part is more correct and aligned with the real-world situation.



using System;
using System.Collections.Generic;
using System.Web;
using EasyAssertions;
using Moq;

namespace MoqTest
{
    class MoqTesting
    {
        static void Main()
        {
            Run();
        }

        private static void Run()
        {
            ClassicalTdd();
            MockistTdd();
        }

        private static void ClassicalTdd()
        {
            var newProduct = new Mock<IProduct>();
            newProduct.SetupGet(p => p.Id).Returns(1);
            newProduct.SetupGet(p => p.Name).Returns("Bushmills");

            newProduct.Object.Name.ShouldBe("Bushmills");

            var productRepository = new Mock<IProductRepository>();
            productRepository
                .Setup(p => p.Get(It.Is<int>(id => id > 0 && id < 6)))
                .Returns(newProduct.Object);

            var productReturned = productRepository.Object.Get(1);
            productReturned.Name.ShouldBe("Bushmills");
        }

        private static void MockistTdd()
        {
            var mockCache = new Mock<ProductCache>();
            var mockDatabase = new Mock<Database>();
            var mockProduct = new Mock<IProduct>();

            mockCache
                .Setup(c => c.Get(1))
                .Returns<IProduct>(null)
                .Verifiable();

            mockDatabase
                .Setup(d => d.Fetch(1))
                .Returns(mockProduct.Object)
                .Verifiable();

            mockCache
                .Setup(c => c.Set(1, mockProduct.Object))
                .Verifiable();

            ProductRepository sut = new ProductRepository(mockCache.Object, mockDatabase.Object);
            sut.GetProduct(1);

            mockCache.Verify();
        }
    }

    public interface IProductRepository
    {
        List<IProductRepository> Select();
        IProduct Get(int id);
    }

    public class ProductRepository : IProductRepository
    {
        private readonly ProductCache _cache;
        private readonly Database _db;

        public ProductRepository(ProductCache cache, Database db)
        {
            _cache = cache;
            _db = db;
        }

        public IProduct GetProduct(int id)
        {
            var product = _cache.Get(id);
            if (product == null)
            {
                product = Get(id);
                _cache.Set(id, product);
            }
            return product;
        }

        public List<IProductRepository> Select()
        {
            throw new NotImplementedException();
        }

        public IProduct Get(int id)
        {
            return _db.Fetch(id);
        }
    }

    public class ProductCache
    {
        public virtual IProduct Get(int id)
        {
            return (IProduct)HttpContext.Current.Cache["product_" + id];
        }

        public virtual void Set(int id, IProduct product)
        {
            HttpContext.Current.Cache["product_" + id] = product;
        }
    }

    public class Database
    {
        public virtual IProduct Fetch(int id)
        {
            return new Product { Id = id };
        }
    }

    public interface IProduct
    {
        int Id { get; set; }
        string Name { get; set; }
    }

    public class Product : IProduct
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}