Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing a two way EF relationship

I'm doing a small practice project to improve my unit testing skills. I'm using Entity Framework Code First.

I'm using a FakeDBSet, which works well for simple lists of entities. When entity trees are returned things aren't so nice. In particular two way relationships aren't maintained as this is part of the Entity Framework magic.

I have two classes:

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

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

If I set the book for a review, the review does not get added to the book's review collection. It does when using EF, but not in my fake version.

Is there a way mock this behaviour, or should I not rely on EF to implement two way relationships? Or is mocking the data context just a waste of time?

like image 615
Richard Garside Avatar asked Dec 17 '12 16:12

Richard Garside


2 Answers

A possible solution I've found is to create a mock object that mimics the EF fixup code.

Here is an example using the Mocking framework 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;
}

This works quite nicely, but I'm not sure if needing to mock this behaviour is an indication that my tests have got too complicated or that my code is making use of side effects that it shouldn't.

I'd be interested in what other people think of this?

like image 43
Richard Garside Avatar answered Oct 05 '22 12:10

Richard Garside


This is actually a pretty common problem (and one without a really good answer). There is a process which happens inside of EF called fixups which runs inside the detect changes loop (the one that triggers on add/remove and a few other changes). This evaluates backlinks in your model. When you start mocking your context you are going to lose that detect changes loop and hence the fixups.

In the past I've gotten around this by understanding this particular limitation of my mocks and making sure i do my setup code in the correct way to make sense in the code (which lets face it is pretty un-ideal). The other option here is to go to some form of real lightweight database in your unit tests and keep using EF.

like image 162
Not loved Avatar answered Oct 05 '22 12:10

Not loved