单元测试双向EF关系

Ric*_*ide 9 unit-testing entity-framework

我正在做一个小练习项目来提高我的单元测试技能.我正在使用Entity Framework Code First.

我正在使用FakeDBSet,它适用于简单的实体列表.当返回实体树时,事情并不那么好.特别是不保持双向关系,因为这是实体框架魔术的一部分.

我有两节课:

public class Book
{
    public virtual ICollection<Review> Reviews {get; set;}
}

public class Review
{
    public virtual Book Book { get; set;}
}
Run Code Online (Sandbox Code Playgroud)

如果我将图书设置为评论,则评论不会添加到图书的评论集中.它在使用EF时会发生,但在我的假版本中却没有.

有没有办法模仿这种行为,还是我不应该依赖EF来实现双向关系?或者嘲笑数据上下文只是浪费时间?

Not*_*ple 6

这实际上是一个非常常见的问题(而且没有一个非常好的答案).在EF内部发生了一个名为fixups的进程,它在检测更改循环内运行(在添加/删除时触发的那个以及一些其他更改).这将评估模型中的反向链接.当你开始模拟你的上下文时,你将失去检测更改循环,从而失去修正.

在过去,我通过理解我的模拟的这个特殊限制并确保我以正确的方式在代码中理解我的设置代码(这让我们面对它非常不理想).这里的另一个选择是在单元测试中使用某种形式的真正轻量级数据库并继续使用EF.

  • @yonexbat模拟的一个关键原因是性能.测试需要一个已知状态,这意味着理想情况下,您应该为每个特定测试设置状态.如果您使用的是真实数据库,则为测试删除和创建数据库的时间可能大约为1秒.如果你将它扩展到超过1000个左右的测试,那么它将变得非常难以管理.IMO边界测试非常重要,这意味着假设EF将按照您的预期执行您的应用程序内部逻辑.无论如何,你正在测试这些场景 (3认同)

Ric*_*ide 5

我发现一个可能的解决方案是创建一个模拟EF修正代码的模拟对象.

以下是使用Mocking框架NSubstitute的示例:

private static Book CreateMockBook()
{
    var book = Substitute.For<Book>();

    // Mock EF fixup: Add a review to collection should also set book for the review
    book.Reviews.Add(Arg.Do<Review>((x) => { if(x.Book != book) x.Book = book; }));

    return book;
}

private static Review CreateMockReview()
{
    var review = Substitute.For<Review>();

    // Mock EF fixup: Set a book for the review should also should add the review to book's review collection
    review.When(x  => x.Book = Arg.Any<Book>()).Do(x => review.Book.Review.Add(review));

    return review;
}
Run Code Online (Sandbox Code Playgroud)

这很好用,但我不确定是否需要模拟这种行为表明我的测试太复杂或我的代码正在利用它不应该的副作用.

我对别人怎么想这个感兴趣?