How to add an item to a Mock DbSet (using Moq)

C#LinqMoqDbset

C# Problem Overview


I'm trying to set up a mock DbSet for testing purposes. I used the tutorial here, http://www.loganfranken.com/blog/517/mocking-dbset-queries-in-ef6/ and slightly modified it so calling GetEnumerator returns a new enumerator each time (another problem i was having). However, I am having difficulty adding items to the DbSet.

The output is preCount = 3 postCount = 3. However, I expect it to be precount = 3 postCount = 4. Any help is greatly appreciated.

static void Main(string[] args)
    {
        Debug.WriteLine("hello debug");

        List<string> stringList = new List<string>
        {
            "a", "b", "c"
        };

        DbSet<string> myDbSet = GetQueryableMockDbSet(stringList);
        int preCount = myDbSet.Count();
        myDbSet.Add("d");
        int postCount = myDbSet.Count();
        Debug.WriteLine("preCount = " + preCount + " postCount = " + postCount);
    }

    private static DbSet<T> GetQueryableMockDbSet<T>(List<T> sourceList) where T : class
    {
        var queryable = sourceList.AsQueryable();

        var dbSet = new Mock<DbSet<T>>();
        dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
        dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
        dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
        dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());

        return dbSet.Object;
    }

C# Solutions


Solution 1 - C#

myDbSet is not real implementation of DbSet but a mock which means it's fake and it needs to be setup for all methods you need. The Add is not exception so it needs to be set up to do what you need otherwise it does nothing.

Add something like the following and when the myDbSet.Add("d"); is called then the 'd' is added to the list and can be returned later.

dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>((s) => sourceList.Add(s));

Complete code

private static DbSet<T> GetQueryableMockDbSet<T>(List<T> sourceList) where T : class
{
	var queryable = sourceList.AsQueryable();

	var dbSet = new Mock<DbSet<T>>();
	dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
	dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
	dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
	dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
	dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>((s) => sourceList.Add(s));

	return dbSet.Object;
}

Output

hello debug
preCount = 3 postCount = 4

Solution 2 - C#

To handle .Find(), we can use reflection in a similar manner, with a few assumptions about conventions when utilizing Find.

(hopefully this isn't out of line, I've had this Q/A bookmarked for years, and was looking for a Find implementation...)

Implement another helper as so:

static object find(IEnumerable<object> oEnumerable, object[] keys)        
{
	// assumptions: primary key of object is named ID
	// primary key of object is an int
	// keys passed to .Find() method is a single value of int type
	foreach (var o in oEnumerable)
	{
		var t = o.GetType();
		var prop = t.GetProperty("ID");
		if (prop != null)
		{
			if (prop.PropertyType == typeof(int))
			{
				if ((int)prop.GetValue(o) == (int)keys[0])
				{
					return o;
				}
			}
		}                
	}
	return null;
}

and in the mock setup example provided by Daniel above:

dbSet.Setup(d => d.Find(It.IsAny<object[]>())).Returns((object[] oArray) => find(sourceList, oArray) as T);

Beacuse we have no way (or desire, usually) to determine the primary key in the way that EF would, I'm making the assumption that "ID" is the key field name (which matches my own conventions), but this could be expanded upon to accept a number of variations. I'm also assuming that only one integer is passed to Find (as is my standard convention), but this could also be expanded on for more robust support.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1080952View Question on Stackoverflow
Solution 1 - C#Daniel DušekView Answer on Stackoverflow
Solution 2 - C#jleachView Answer on Stackoverflow