如何对DbSet.Find进行存根测试

3
// acknowledgement: https://dev59.com/DnA75IYBdhLWcg3wH1RB#5022512
public class FakeDbSet<T> : IDbSet<T> where T : class
{
    private readonly HashSet<T> data;
    private readonly IQueryable query;

    public FakeDbSet()
    {
        data = new HashSet<T>();
        query = data.AsQueryable();
    }

    public virtual T Find(params object[] keyValues)
    {
        throw new NotImplementedException();
    }
}

我该如何实现Find方法?

为了在Find方法中执行主键值比较,我需要确定T的主键值,但我不知道如何做。


1
你能在存根类中添加dbContext实例吗? - Yuliam Chandra
@CasterTroy,如果您有一个DbContext实例,那么就有一些方法可以让您检索给定实体的键。一旦您知道哪些是关键属性,您就可以在虚拟的HashSet<T>中查找这些实体。但我猜您没有一个DbContext实例,因为据我所知,这个类的目的是模拟数据库本身。 - Darin Dimitrov
将DbContext添加到测试类可能不太合适,只是为了向您展示此链接以获取主键属性名称(https://dev59.com/Z2w05IYBdhLWcg3w_Guw),然后从那里可以使用反射获取值。 - Yuliam Chandra
@DarinDimitrov 我不这样做。我通过接口公开每个IDbSet - Caster Troy
1
那么实现这个的唯一方法就是你在第一条评论中已经建议的。让你的实体实现一个接口,指示哪个是关键属性。但是如果你有具有复杂主键的实体,事情可能会变得棘手。 - Darin Dimitrov
显示剩余2条评论
3个回答

2

试试这个:

public class FakeDbSet<T> : IDbSet<T> where T : class
{
    private Func<T, object[], bool> _findSelector
    private readonly HashSet<T> data;
    private readonly IQueryable query;

    public FakeDbSet(Func<T, object[], bool> findSelector)
    {
        _findSelector = findSelector;
        data = new HashSet<T>();
        query = data.AsQueryable();
    }

    public virtual T Find(params object[] keyValues)
    {
        return _data.SingleOrDefault(item => _findSelector(item, keyValues));
    }
}

谢谢,这回答了我的问题。然而,我不会真正使用它 - 它很糟糕。相反,我将采用一种新的设计策略。 - Caster Troy

1
如果我理解正确,您需要为单元测试目的存根DbSet对象的Find()方法。
如果您使用的是EntityFramework 6或更高版本,并且使用诸如Moq、NSubstitute或FakeItEasy之类的模拟框架,那么您可以轻松实现这一点,而无需编写像FakeDbSet这样的类。
本文提供了有关存根/模拟过程的完整说明: http://msdn.microsoft.com/en-us/data/dn314429.aspx 并提供了一个使用此文章中的类的示例。
    [Test]
    public void Foo_WhenCalled_CallsDb()
    {
        //Arrange
        var fakeDbSet = new Mock<DbSet<Course>>();
        fakeDbSet.Setup(dbs => dbs.Find(It.IsAny<object>())).Returns(new Course { CourseID = 125 });

        var fakeContext = new Mock<SchoolEntities>();
        fakeContext.Setup(c => c.Courses).Returns(fakeDbSet.Object);


        var foo = new Foo(fakeContext.Object);

        //Act
        string result = foo.MyMethod();

        //Assert
        Assert.AreEqual("125", result);
    }

对于其他版本的EntityFramework,考虑使用“仓储”模式 - 它提供了很好的测试能力。希望我的回答能够帮到你。

谢谢。你不可能知道这一点,但对我来说这不适合,因为我已经在对抗一个填充了虚假存储库的环境。 - Caster Troy

0

重写该方法

public class FakePostsDbSet : FakeDbSet<Post>
{
    public override Post Find(params object[] keyValues)
    {
        return this.SingleOrDefault(
            post => post.Slug == (string) keyValues.Single());
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接