68

我正在尝试设置一个模拟 DbSet 用于测试目的。我在这里使用了教程,http://www.loganfranken.com/blog/517/mocking-dbset-queries-in-ef6/并对其稍作修改,因此每次调用 GetEnumerator 都会返回一个新的枚举器(我遇到的另一个问题) . 但是,我很难将项目添加到 DbSet。

输出是 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;
    }
4

2 回答 2

145

myDbSet不是真正的实现,DbSet而是一个模拟,这意味着它是的,需要为您需要的所有方法进行设置。也不例外,Add因此需要将其设置为执行您需要的操作,否则它什么也不做。

添加类似于以下内容的内容,当myDbSet.Add("d");调用 'd' 时,会将其添加到列表中,并且可以稍后返回。

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

完整代码

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

输出

hello debug
preCount = 3 postCount = 4
于 2015-07-10T21:33:43.697 回答
1

为了处理.Find(),我们可以以类似的方式使用反射,在使用Find.

(希望这并不过分,我已经把这个 Q/A 收藏了很多年,并且正在寻找一个Find实现......)

像这样实现另一个助手:

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

在上面 Daniel 提供的模拟设置示例中:

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

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

于 2020-08-13T11:56:04.967 回答