Click here to Skip to main content
15,879,535 members
Articles / DevOps / Unit Testing

Moq - Mock Database

Rate me:
Please Sign up or sign in to vote.
5.00/5 (8 votes)
18 Oct 2012CPOL4 min read 136.7K   30   8
Mocking database in Unit Tests using Moq.

Introduction

Moq is a very useful framework which easily mocks service calls and methods for your unit testing. 

This article helps you to understand Moq with respect to mocking a database (i.e. writing unit test cases for your repository project). 

Here I have used Microsoft Enterprise Library objects (to make it easy to understand) you can very well extend it to any other framework, util or ADO.NET methods. I will also try to cover some advanced concepts used in Moq like anonymous methods, Callback() and Queueing.    

Background

I have been using Moq since last, almost an year and found that many people struggle or find difficult to mock databases. Many of us use Dev instance of database and make our test cases call the actual SQL Instance. 

Using the code

First things first – Your repository should have a constructor (or a public property)  through which you can pass the mocked database object from the unit test.

Below is sample of such constructor:-

C#
public MyRepository(Databse Db)
{
  this.database = Db;
}

Below is sample of an "ExecuteScalar" method (it returns number of employees in a certain location).

C#
using (DbCommand cmd = database.GetStoredProcCommand(SPCREATEPRODUCTLIST))           
{ 
    this.database.AddParameter(cmd, "@Location", DbType.String, 0, 
      ParameterDirection.Input, true, 0, 0, "Location", DataRowVersion.Default, location);
    object result = database.ExecuteScalar(cmd);
}

This is how you can mock a scalar method:

C#
private static Mock<Database> MockExecuteScalar(object returnValue)
{
   Mock<DbProviderFactory> mockedDBFactory = new Mock<DbProviderFactory>();
   Mock<Database> mockedDB = new Mock<Database>("MockedDB", mockedDBFactory.Object);
   mockedDB.Setup(x => x.ExecuteScalar(It.IsAny<DbCommand>())).Returns(returnValue);
   return mockedDB;
}

(You can read more about Enterprise library and its implementations at http://msdn.microsoft.com/en-us/library/ff648951.aspx ).

This is quite straight forward, this method mocks the "ExecuteScalar" method (since this method is mentioned as virtual in Database class you are able to mock it. You can only mock Interfaces easily, while mocking a class you can only mock virtual properties and  methods

Below is how you will call this in your unit test case:

C#
Database mockedDB = MockExecuteScalar("5").Object;
MyRepository target = new MyRepository(mockedDB);
var result = target.GetEmployeeCount("London");

In the same way you can mock "ExecuteNonQuery" implementations:

C#
private static Mock<Database> MockExecuteNonQuery(object returnValue)
{
   Mock<DbProviderFactory> mockedDBFactory = new Mock<DbProviderFactory>();
   Mock<Database> mockedDB = new Mock<Database>("MockedDB", mockedDBFactory.Object);
   mockedDB.Setup(x => x.ExecuteNonQuery(It.IsAny<DbCommand>())).Returns(1);         
   return mockedDB;
}

Now, let's move on to "ExecuteReader" implementations. ExecuteReader can be a collection of rows and we loop the DataReader stream till the end of data. So here there are two functions to mock.

  1. ExecuteReader() - To get the actual data
  2. Read() - To return true till we get the desired data

Below is example of a typical implementation using "ExecuteReader":

C#
using (DbCommand cmd = database.GetStoredProcCommand("GetEmployeeDetails", parameters))
{
    using (IDataReader dr = database.ExecuteReader(cmd))
    {
        while (dr.Read())
        {
           listofEmployeeDetails.Add(new Employee
           {
                EmployeeId = dr["EmpID"].ToString();
                EmployeeName = dr["EmployeeName"].toString();
                Location = dr["Location"].toString(); 
           });           
        }
    }
}

First let's see a simple example where  we will mock "ExecuteReader" to return a single row of data from our MockedDatabase:

Step 1: Mock "Read" method

Before mocking read method I would like to brief you about anonymous methods in Moq functions and Callback() method.

Callback()

We have already seen the .Returns() method which returns response for a mocked function call. If you want to execute your custom logic after the control comes back from Return() you can use Callback().   

This will look something like below:

C#
mockedObject.Setup(x=>x.myMethod(It.IsAny<string>())).Returns("Hello").Callback(//custom logic goes here);

Anonymous Methods

Anonymous methods come handy you are calling a mocked method multiple times and want to change the return value  dynamically.

Below is an example:

C#
string returnValue = "Hello"  
mockedObject.Setup(x=>x.myMethod(It.IsAny<string>())).Returns(()=>returnValue).Callback(()=>returnValue="World");

When we call "myMethod" for the very first time, the return value will be "Hello" from second time onward it will return "World". You can put any conditions or your custom implementation inside this anonymous method to suit your needs.

Now in this scenario we want "ExecuteReader" method to read one row of data. So in that case dataReader.Read() method should return true 1st time only.

So, we can mock .Read() method like:

C#
var mockedDataReader = new Mock<IDataReader>();
bool readFlag = true;
mockedDataReader.Setup(x => x.Read()).Returns(() => readFlag).Callback(() => readFlag = false);

Step 2: Mock ExecuteReader

Before we mock "ExecuteReader" method we need to setup the response data. So when I call dr["EmpID"] 

I get my desired mocked value. We can achieve this like below:-
C#
mockedDataReader.Setup(x => x["EmpID"]).Returns("43527");  
mockedDataReader.Setup(x => x["EmployeeName"]).Returns("Smith");  
mockedDataReader.Setup(x => x["Location"]).Returns("London"); 
Now we will mock the "ExecuteReader" method, which will return our mocked object.
C#
Mock<DbProviderFactory> mockedDBFactory = new Mock<DbProviderFactory>();
Mock<Database> mockedDB = new Mock<Database>("MockedDB", mockedDBFactory.Object);
mockedDB.Setup(x => x.ExecuteReader(It.IsAny<DbCommand>())).Returns(mockedDataReader.Object);
The above way is same like "ExecuteScalar" and "ExecuteNonQuery" but here we are returning our custom DataReader object. Below is how the complete method will look like.
C#
private static Mock<Database> MockExecuteReader(Dictionary<string, object> returnValues)
{
    var mockedDataReader = new Mock<IDataReader>();
    bool readFlag = true;
    mockedDataReader.Setup(x => x.Read()).Returns(() => readFlag).Callback(() => readFlag = false);
    foreach (KeyValuePair<string, object> keyVal in returnValues)
    {
        mockedDataReader.Setup(x => x[keyVal.Key]).Returns(keyVal.Value);
    }          
    Mock<DbProviderFactory> mockedDBFactory = new Mock<DbProviderFactory>();
    Mock<Database> mockedDB = new Mock<Database>("MockedDB", mockedDBFactory.Object);
    mockedDB.Setup(x => x.ExecuteReader(It.IsAny<DbCommand>())).Returns(mockedDataReader.Object);
    return mockedDB;
} 

There might be cases where you want to select multiple rows from database.

Before I start explaining about mocking multiple rows, let me explain one tricky thing in Return() function.

Let say I mocked a method, which I am calling multiple times in my code.

C#
mockedService.Setup(x=>x.myMethod(It.IsAny<string>())).Returns("First");
mockedService.Setup(x=>x.myMethod(It.IsAny<string>())).Returns("Second"); 
mockedService.Setup(x=>x.myMethod(It.IsAny<string>())).Returns("Third"); 
The code above might look OK at first glance. But it will give "Third" as output every time.

Here anonymous functions come real handy but we need to ensure that we get output in certain order. We can achieve it by using Queue. The code will look something like this:-

C#
Queue<object> responseQueue = new Queue<object>();
responseQueue.Enqueue("First");
responseQueue.Enqueue("Second");
responseQueue.Enqueue("Third");
mockedService.Setup(x=>x.myMethod(It.IsAny<string>())).Returns(()=>responseQueue.Dequeue()); 
If you observe the Returns() method will now invoke an anonymous method which will dequeue the values one by one. 

For returning multiple rows we will need something similar where we need to Dequeue() each row one by one. The completed method will look like below:- 

C#
private static Mock<Database> MockExecuteReader(List<Dictionary<string, object>> returnValues)
{
  var mockedDataReader = new Mock<IDataReader>();
  int count = 0;
  Queue<object> responseQueue = new Queue<object>();
  mockedDataReader.Setup(x => x.Read()).Returns(() => count<returnValues.Count).Callback(() => count++);
  returnValues.ForEach(rows =>
  {
   foreach (KeyValuePair<string, object> keyVal in rows)
   {
       responseQueue.Enqueue(keyVal.Value);
       mockedDataReader.Setup(x => x[keyVal.Key]).Returns(()=>responseQueue.Dequeue());
   }
  });
  Mock<DbProviderFactory> mockedDBFactory = new Mock<DbProviderFactory>();
  Mock<Database> mockedDB = new Mock<Database>("MockedDB", mockedDBFactory.Object);
  mockedDB.Setup(x => x.ExecuteReader(It.IsAny<DbCommand>())).Returns(mockedDataReader.Object);
  return mockedDB;
}

If you observe the mocking of Read(), it is based on the length of the no. of mocked datarows you want to return (length of the List<>).

On each Callback() a local variable count is incremented so that when it exceeds the number of datarows Read() method will return false. 

You can implement the techniques of anonymous methods, Callback method and Queuing in all your unit tests. While mocking Repositories you can use these generic methods to Mock your databases.

Download Moq - (http://code.google.com/p/moq/)    

Happy mocking Smile | :)

License

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


Written By
Software Developer (Senior)
India India
I have been working as a developer in SOA and SaaS based projects.
Have worked mainly on Asp.Net, ajax, jQuery,c#, .Net 3.5 and 4.0.
You can reach me on ip_28@yahoo.co.in

Comments and Discussions

 
QuestionNot Able To Mock When we have parameters for Stored Procedures Pin
vijay kumar sahu9-Oct-22 18:37
vijay kumar sahu9-Oct-22 18:37 
QuestionShouldn't database tests be state dependent? Pin
amolspider23-Apr-14 18:05
amolspider23-Apr-14 18:05 
QuestionMock NextResult Pin
Member 990453512-Mar-13 4:09
Member 990453512-Mar-13 4:09 
AnswerRe: Mock NextResult Pin
Indranil Pal12-Mar-13 5:35
Indranil Pal12-Mar-13 5:35 
AnswerRe: Mock NextResult Pin
Indranil Pal12-Mar-13 20:19
Indranil Pal12-Mar-13 20:19 
GeneralMy vote of 5 Pin
Felix De Herrera26-Dec-12 7:29
Felix De Herrera26-Dec-12 7:29 
GeneralRe: My vote of 5 Pin
Indranil Pal26-Dec-12 8:15
Indranil Pal26-Dec-12 8:15 
GeneralMy vote of 5 Pin
Florian.Witteler23-Oct-12 23:29
Florian.Witteler23-Oct-12 23:29 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.