9

我正在做一个小型实践项目来提高我的单元测试技能。我首先使用实体​​框架代码。

我正在使用 FakeDBSet,它适用于简单的实体列表。当返回实体树时,事情就不那么好了。特别是不维护两种方式的关系,因为这是实体框架魔法的一部分。

我有两节课:

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

public class Review
{
    public virtual Book Book { get; set;}
}

如果我将书设置为评论,则评论不会添加到该书的评论集中。使用 EF 时会这样做,但在我的假版本中不会。

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

4

2 回答 2

6

这实际上是一个非常普遍的问题(并且没有一个很好的答案)。在 EF 内部发生了一个称为 fixups 的过程,该过程在检测更改循环内运行(在添加/删除和一些其他更改时触发的那个)。这会评估模型中的反向链接。当您开始模拟您的上下文时,您将丢失检测更改循环以及因此的修复。

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

于 2012-12-17T19:44:29.310 回答
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;
}

这工作得很好,但我不确定是否需要模拟这种行为是否表明我的测试过于复杂,或者我的代码正在使用它不应该使用的副作用。

我很想知道其他人对此有何看法?

于 2012-12-21T13:26:49.853 回答