The concept of unit testing my code is still fairly new to me and was introduced when I started writing applications with the Microsoft MVC Framework in Visual Studio 2008. Intimidated somewhat by the Moq library's heavy reliance on lambdas, my early tests used full Mock classes that I would write myself, and which implemented the same interface
as my real database repositories. I'd only write the code for the methods I needed, all other methods would simply throw a "NotImplementedException
". However, I quickly discovered that the problem with this approach is that whenever a new method was added to the interface
, my test project would no longer build (since the new method was not implemented in my mock repository) and I would have to manually add a new method that threw another "NotImplementedException
". After doing this for the 5th or 6th time, I decided to face my fears and get to grips with using the Moq library instead. Here is a simple example, of how you can mock a database repository class using the Moq library.
Let's assume that your database contains a table called Product
, and that either you or Linq, or LLBLGen, or something similar has created the following class to represent that table as an object in your class library.
The Product Class
namespace MoqRepositorySample
{
using System;
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
}
}
Your Product
Repository class might implement an interface
similar to the following, which offers basic database functionality such as retrieving a product by id, by name, fetching all products, and a save method that would handle inserting and updating products.
The IProductRepository Interface
namespace MoqRepositorySample
{
using System.Collections.Generic;
public interface IProductRepository
{
IList<Product> FindAll();
Product FindByName(string productName);
Product FindById(int productId);
bool Save(Product target);
}
}
The test class that follows demonstrates how to use Moq to set up a mock Products
repository based on the interface
above. The unit tests shown here focus primarily on testing the mock repository itself, rather than on testing how your application uses the repository, as they would in the real world.
Microsoft Unit Test Class
namespace TestProject1
{
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using MoqRepositorySample;
[TestClass]
public class UnitTest1
{
public UnitTest1()
{
IList<Product> products = new List<Product>
{
new Product { ProductId = 1, Name = "C# Unleashed",
Description = "Short description here", Price = 49.99 },
new Product { ProductId = 2, Name = "ASP.Net Unleashed",
Description = "Short description here", Price = 59.99 },
new Product { ProductId = 3, Name = "Silverlight Unleashed",
Description = "Short description here", Price = 29.99 }
};
Mock<IProductRepository> mockProductRepository = new Mock<IProductRepository>();
mockProductRepository.Setup(mr => mr.FindAll()).Returns(products);
mockProductRepository.Setup(mr => mr.FindById(
It.IsAny<int>())).Returns((int i) => products.Where(
x => x.ProductId == i).Single());
mockProductRepository.Setup(mr => mr.FindByName(
It.IsAny<string>())).Returns((string s) => products.Where(
x => x.Name == s).Single());
mockProductRepository.Setup(mr => mr.Save(It.IsAny<Product>())).Returns(
(Product target) =>
{
DateTime now = DateTime.Now;
if (target.ProductId.Equals(default(int)))
{
target.DateCreated = now;
target.DateModified = now;
target.ProductId = products.Count() + 1;
products.Add(target);
}
else
{
var original = products.Where(
q => q.ProductId == target.ProductId).Single();
if (original == null)
{
return false;
}
original.Name = target.Name;
original.Price = target.Price;
original.Description = target.Description;
original.DateModified = now;
}
return true;
});
this.MockProductsRepository = mockProductRepository.Object;
}
public TestContext TestContext { get; set; }
public readonly IProductRepository MockProductsRepository;
[TestMethod]
public void CanReturnProductById()
{
Product testProduct = this.MockProductsRepository.FindById(2);
Assert.IsNotNull(testProduct);
Assert.IsInstanceOfType(testProduct, typeof(Product));
Assert.AreEqual("ASP.Net Unleashed", testProduct.Name);
}
[TestMethod]
public void CanReturnProductByName()
{
Product testProduct = this.MockProductsRepository.FindByName("Silverlight Unleashed");
Assert.IsNotNull(testProduct);
Assert.IsInstanceOfType(testProduct, typeof(Product));
Assert.AreEqual(3, testProduct.ProductId);
}
[TestMethod]
public void CanReturnAllProducts()
{
IList<Product> testProducts = this.MockProductsRepository.FindAll();
Assert.IsNotNull(testProducts);
Assert.AreEqual(3, testProducts.Count);
}
[TestMethod]
public void CanInsertProduct()
{
Product newProduct = new Product
{ Name = "Pro C#", Description = "Short description here", Price = 39.99 };
int productCount = this.MockProductsRepository.FindAll().Count;
Assert.AreEqual(3, productCount);
this.MockProductsRepository.Save(newProduct);
productCount = this.MockProductsRepository.FindAll().Count;
Assert.AreEqual(4, productCount);
Product testProduct = this.MockProductsRepository.FindByName("Pro C#");
Assert.IsNotNull(testProduct);
Assert.IsInstanceOfType(testProduct, typeof(Product));
Assert.AreEqual(4, testProduct.ProductId);
}
[TestMethod]
public void CanUpdateProduct()
{
Product testProduct = this.MockProductsRepository.FindById(1);
testProduct.Name = "C# 3.5 Unleashed";
this.MockProductsRepository.Save(testProduct);
Assert.AreEqual("C# 3.5 Unleashed", this.MockProductsRepository.FindById(1).Name);
}
}
}