Fakes, Mocks, and Stubs
February 22, 2010 Leave a comment
I have started looking at MOQ more closely. I found there was a dearth of material. The best article on how to implement a basic Mock is found here. Note that tutorials found on the Mock homepage by Nixusg and Steve Walther use an older version of Mock and do not work.
I was still a bit confused of WHY I would want to use a Mock versus a Fake when I came across a great explanation in Pragmatic Unit Testing on page 104. Basically, it boils down to the size of your project and what you need to test. If you have a rudimentary class, you can just create a fake object in your test’s setup and assign it all of the needed properties. If you are testing behavior of a class, you can set up the dependent object that the behavior depends on (and use DI) with the way you want to tests to perform. For example, I have a basic DTO like this:
public class Product
{
public Product() { }
public Product(int productId, string productName, string description,
double price, DateTime dateCreated, DateTime dateModified)
{
this.ProductId = productId;
this.ProductName = productName;
this.Description = description;
this.Price = price;
this.DateCreated = dateCreated;
this.DateModified = DateModified;
}
public int ProductId { get; set; }
public string ProductName { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
public override string ToString()
{
return String.Format("{0} : {1}", ProductId, ProductName);
}
}
And I have an Interface for a Builder that works on the DTO like this:
public interface IProductRepository
{
IList<Product> FindAll();
Product FindByName(string productName);
Product FindById(int productId);
bool Save(Product product);
}
I then added a Test Project to my solution and added 2 Testing Classes – One using a Fake and one using a Mock. The Fake was set up like this:
class TestProductRepository : IProductRepository
{
List<Product> products = new List<Product>();
public TestProductRepository()
{
products.Add(new Product(1, "Test Product One",
"Description For One",12.4, DateTime.Now, DateTime.Now));
products.Add(new Product(2, "Test Product Two",
"Description For Two",1.8, DateTime.Now, DateTime.Now));
products.Add(new Product(3, "Test Product Trhee",
"Description For Three",6.5, DateTime.Now, DateTime.Now));
}
public IList<Product> FindAll()
{
return products;
}
public Product FindByName(string productName)
{
foreach (Product product in products)
{
if (product.ProductName == productName)
{
return product;
}
}
return null;
}
With the Unit tests like this
[TestMethod()]
public void FindAllTest()
{
IProductRepository target = CreateIProductRepository();
int expected = 3;
int actual = target.FindAll().Count;
Assert.AreEqual(expected, actual);
}
The problem comes in when the interface that is tested has an enormous number of methods and properties. Using Mocking, you can set up you Mock and only worry about the properties and methods that you care about.
Consider the same tests with a Mock:
internal virtual Mock<IProductRepository> CreateIProductRepositoryMock()
{
IList<Product> products = new List<Product>
{
new Product {ProductId=1, ProductName="Product One",
Description = "Product One Description", Price=5.6},
new Product {ProductId=2, ProductName="Product Two",
Description = "Product Two Description", Price=6.7},
new Product {ProductId=3, ProductName="Product Three",
Description = "Product Three Description", Price=2.1}
};
Mock<IProductRepository> mockIProductRepository =
new Mock<IProductRepository>();
mockIProductRepository.Setup(mr =>
mr.FindAll()).Returns(products);
mockIProductRepository.Setup(mr => mr.FindById(It.IsAny<int>()))
.Returns((int productId) =>
new Product { ProductId = productId,
ProductName = "Product One Description" });
mockIProductRepository.Setup(mr =>
mr.FindByName(It.IsAny<string>()))
.Returns((string productName) =>
new Product { ProductId = 1, ProductName = productName });
mockIProductRepository.Setup(mr =>
mr.Save(It.IsAny<Product>())).Returns(true);
return mockIProductRepository;
}
With the Unit Tests like this
[TestMethod()]
public void FindAllTest()
{
Mock<IProductRepository> mock = CreateIProductRepositoryMock();
int expected = 3;
int actual = mock.Object.FindAll().Count;
Assert.AreEqual(expected, actual);
}
Even in this basic example, you can see how fewer line of code the Mock took and how much more control the Lambda gives you. Mock was harder because I was not as familiar with the Lambda. However, once I figured out the Lambda, I started admiring how powerful Mocking can be. You definitely can write better and more comprehensive test with Mocks.