Click here to Skip to main content
Click here to Skip to main content

Mock a database repository using Moq

By , 15 Dec 2009
 

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 lambadas, 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;

    /// <summary>
    /// Summary description for UnitTest1
    /// </summary>
    [TestClass]
    public class UnitTest1
    {
        /// <summary>
        /// Constructor
        /// </summary>
        public UnitTest1()
        {
            // create some mock products to play with
            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 the Products Repository using Moq
            Mock<IProductRepository> mockProductRepository = new Mock<IProductRepository>();

            // Return all the products
            mockProductRepository.Setup(mr => mr.FindAll()).Returns(products);

            // return a product by Id
            mockProductRepository.Setup(mr => mr.FindById(
                It.IsAny<int>())).Returns((int i) => products.Where(
                x => x.ProductId == i).Single());

            // return a product by Name
            mockProductRepository.Setup(mr => mr.FindByName(
                It.IsAny<string>())).Returns((string s) => products.Where(
                x => x.Name == s).Single());

            // Allows us to test saving a product
            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;
                });
 
            // Complete the setup of our Mock Product Repository
            this.MockProductsRepository = mockProductRepository.Object;
        }
 
        /// <summary>
        /// Gets or sets the test context which provides
        /// information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext { get; set; }
 
        /// <summary>
        /// Our Mock Products Repository for use in testing
        /// </summary>
        public readonly IProductRepository MockProductsRepository;
 
        /// <summary>
        /// Can we return a product By Id?
        /// </summary>
        [TestMethod]
        public void CanReturnProductById()
        {
            // Try finding a product by id
            Product testProduct = this.MockProductsRepository.FindById(2);
 
            Assert.IsNotNull(testProduct); // Test if null
            Assert.IsInstanceOfType(testProduct, typeof(Product)); // Test type
            Assert.AreEqual("ASP.Net Unleashed", testProduct.Name); // Verify it is the right product
        }
 
        /// <summary>
        /// Can we return a product By Name?
        /// </summary>
        [TestMethod]
        public void CanReturnProductByName()
        {
            // Try finding a product by Name
            Product testProduct = this.MockProductsRepository.FindByName("Silverlight Unleashed");
 
            Assert.IsNotNull(testProduct); // Test if null
            Assert.IsInstanceOfType(testProduct, typeof(Product)); // Test type
            Assert.AreEqual(3, testProduct.ProductId); // Verify it is the right product
        }
 
        /// <summary>
        /// Can we return all products?
        /// </summary>
        [TestMethod]
        public void CanReturnAllProducts()
        {
            // Try finding all products
            IList<Product> testProducts = this.MockProductsRepository.FindAll();
 
            Assert.IsNotNull(testProducts); // Test if null
            Assert.AreEqual(3, testProducts.Count); // Verify the correct Number
        }
 
        /// <summary>
        /// Can we insert a new product?
        /// </summary>
        [TestMethod]
        public void CanInsertProduct()
        {
            // Create a new product, not I do not supply an id
            Product newProduct = new Product
                { Name = "Pro C#", Description = "Short description here", Price = 39.99 };
 
            int productCount = this.MockProductsRepository.FindAll().Count;
            Assert.AreEqual(3, productCount); // Verify the expected Number pre-insert
 
            // try saving our new product
            this.MockProductsRepository.Save(newProduct);
 
            // demand a recount
            productCount = this.MockProductsRepository.FindAll().Count;
            Assert.AreEqual(4, productCount); // Verify the expected Number post-insert
 
            // verify that our new product has been saved
            Product testProduct = this.MockProductsRepository.FindByName("Pro C#");
            Assert.IsNotNull(testProduct); // Test if null
            Assert.IsInstanceOfType(testProduct, typeof(Product)); // Test type
            Assert.AreEqual(4, testProduct.ProductId); // Verify it has the expected productid
        }
 
        /// <summary>
        /// Can we update a prodict?
        /// </summary>
        [TestMethod]
        public void CanUpdateProduct()
        {
            // Find a product by id
            Product testProduct = this.MockProductsRepository.FindById(1);
 
            // Change one of its properties
            testProduct.Name = "C# 3.5 Unleashed";
 
            // Save our changes.
            this.MockProductsRepository.Save(testProduct);
 
            // Verify the change
            Assert.AreEqual("C# 3.5 Unleashed", this.MockProductsRepository.FindById(1).Name);
        }
    }
}

Download the Sample project and run the tests yourself:

MoqRepositorySample.zip (691.96 kb)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Williarob
Software Developer (Senior) Salem Web Network
United States United States
Member
Robert Williams has been programming web sites since 1996 and employed as .NET developer since its release in 2002.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberblackninja720 Mar '13 - 14:36 
Great Article, Thanks a lot.
GeneralFantastic ArticlememberAllen Conway1 Mar '13 - 10:33 
Need I say more, this is a great article for displaying syntactically how to mock a generic repository using Moq. Big Grin | :-D
Questionprice datatype!memberjalalx5 Apr '12 - 21:32 
Hi,
Thanks, but you should use Decimal datatype instate of Double datatype! This article talks about unit testing and it's important to choose correct datatype for fileds that may be subject of unit testing Wink | ;) .
The philosophers have only interpreted the world in various ways; The point however is to change it!
-Karl Marx-

GeneralThat's not a test!memberjanus00713 Jan '11 - 9:26 
You are using your mocking your objects in such a way that they that they don't test anything OMG | :OMG: When you are mocking you should use your mocked objects in one or more methods.
 
You mentioned it though, but I thought it's better to avoid any confusions.
GeneralRe: That's not a test!membermakv200918 Oct '11 - 0:03 
Could you tell me , what is wrong with this approach?
GeneralRe: That's not a test!memberAurimas21 Mar '12 - 22:31 
Here tests are used to test mock itself. It doesn't test any classes of your code. This mocked repository should be used to test class that is dependent on IRepository interface.
GeneralRhino vs moqmemberSohel_Rana18 Dec '09 - 21:04 
I have used Rhino but recently started using moq. Moq is really much more interesting than Rhino as it supports linq. But I found Rhino syntax much more easier than moq.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 15 Dec 2009
Article Copyright 2009 by Williarob
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid