简体   繁体   English

如何将项目添加到 Mock DbSet(使用 Moq)

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

I'm trying to set up a mock DbSet for testing purposes.我正在尝试设置一个模拟 DbSet 以进行测试。 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).我在这里使用了教程http://www.loganfranken.com/blog/517/mocking-dbset-queries-in-ef6/并稍微修改了它,因此每次调用 GetEnumerator 都会返回一个新的枚举器(我遇到的另一个问题) . However, I am having difficulty adding items to the DbSet.但是,我很难将项目添加到 DbSet。

The output is preCount = 3 postCount = 3. However, I expect it to be precount = 3 postCount = 4. Any help is greatly appreciated.输出是 preCount = 3 postCount = 3。但是,我希望它是 precount = 3 postCount = 4。非常感谢任何帮助。

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;
    }

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. myDbSet是不是真正落实DbSet而是一个模拟的,这意味着它的假货,它需要设置你所需要的所有方法。 The Add is not exception so it needs to be set up to do what you need otherwise it does nothing. Add也不例外,因此需要对其进行设置以执行您需要的操作,否则它什么也不做。

Add something like the following and when the myDbSet.Add("d");添加类似以下内容和myDbSet.Add("d"); is called then the 'd' is added to the list and can be returned later.被调用,然后将 'd' 添加到列表中,稍后可以返回。

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

To handle .Find() , we can use reflection in a similar manner, with a few assumptions about conventions when utilizing Find .为了处理.Find() ,我们可以以类似的方式使用反射,在使用Find时有一些关于约定的假设。

(hopefully this isn't out of line, I've had this Q/A bookmarked for years, and was looking for a Find implementation...) (希望这并没有出格,我已经将这个 Q/A 加入书签多年,并且一直在寻找Find实现......)

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:在上面 Daniel 提供的模拟设置示例中:

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.因为我们无法(或通常希望)以 EF 的方式确定主键,所以我假设“ID”是键字段名称(符合我自己的约定),但这可能是扩展以接受许多变体。 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.我还假设只有一个整数被传递给Find (这是我的标准约定),但这也可以扩展以获得更强大的支持。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM