1

Edit: I forgot to mention that I'm using a Code First approach

I'm currently developing an application which uses the Entity Framework. The system currently has restaurants (Called Subjects in my code), users and reviews.

I'm brand new to using the Entity Framework, so I was wondering on how I can set up unit tests correctly. I'm assuming I don't have to test any inserting, retrieving and modifying data coming from EF, since this is probably tested already. When I want to set up tests for the code I'm writing, how should I handle objects coming from EF?

I'll give an example of a unit test I have developed. I'm using a repository structure for calls to the database. I'm aware this isn't necessary, but I like the abstraction in case we're switching databases.

My repository looks like this. (The user repository basically follows the same idea)

public class SubjectRepository
{
    private readonly RecommenderContext _context;

    public SubjectRepository()
    {
        _context = new RecommenderContext();
    }

    public Subject FindByGuid(Guid subjectId)
    {
        return _context.Subjects.Where(x => x.Id == subjectId).FirstOrDefault();
    }

    public void AddFollower(User userFollowing, Subject subjectToFollow)
    {
        _context.Subjects.Where(x => x.Id == subjectToFollow.Id).FirstOrDefault().Followers.Add(userFollowing);
        _context.SaveChanges();
    }

    public void CreateSubject(Subject sub)
    {
        _context.Subjects.Add(sub);
        _context.SaveChanges();
    }
}

And this is my test class

[TestClass]
public class SubjectRepositoryTests
{
    SubjectRepository _subjectRepository;
    UserRepository _userRepository;

    [TestInitialize]
    public void init()
    {
        _subjectRepository = new SubjectRepository();
        _userRepository = new UserRepository();
    }

    [TestMethod]
    public void AddFollower()
    {
        Subject restaurant = Subject.CreateRandomSubject(); //These functions return randomized objects used for testing
        User activeUser = User.CreateRandomUser();

        _subjectRepository.CreateSubject(restaurant);
        _userRepository.CreateUser(activeUser);

        _subjectRepository.AddFollower(activeUser, restaurant);

        Assert.AreEqual(activeUser.Id, _subjectRepository.FindByGuid(restaurant.Id).Followers[0].Id);
    }
} 

Is this a correct way of setting up an unit test? And if so, how would I go around correctly cleaning up the objects I have inserted?

Any other feedback on how to use unit tests in my scenario better would be greatly appreciated!

2 Answers 2

2

Assuming you are using codefirst...

I would avoid having the proper RecommenderContext with direct database calls in it used in the tests. Replace it with an interface inside your repositories, and create a fake implementation that can be injected into your repository for testing. Then you don't have to worry about leaving residue or connecting to the db in tests.

You can use one of the FakeDbSet classes to implement the test context. Effectively this would test your repository logic whilst ignoring the built in EF behaviour, and leave your test nicely isolated.

Sign up to request clarification or add additional context in comments.

6 Comments

Yes, I'm using codefirst, forgot to mention! So instead of using my context class, I would have to create a new class with FakeDbSets to use for tests? Or can I just add these fakedbsets to the existing context? Also what do you mean with avoiding the RecommenderContext with direct database calls? Could you elaborate?
Yes, thats it. You create an IRecommenderContext interface. And then a new FakeRecommenderContext implementation. So you don't rely on the tests calling the database, instead you rely on an in memory fake context. This isolates your tests from all kinds of trouble that can come from making direct database calls in them.
I only mean avoid Using the recommendercontext which will make the direct database calls in the test. You can of course use this in your usual way in the app. Its just for unit testing its generally good to isolate yourself away from external dependencies like the database.
Alright that makes sense, thank you! This might be a weird question, but can you provide a sample code on how to use a fake data set? I'm brand new to the EF, so I'm clueless on how to use these fake sets. Is it literally just FakeDbSet<Subject> ?
To be honest its been a while since I last did this, I think I used something like... nuget.org/packages/FakeDbSet
|
0

From a purist point of view you are not unit testing in the example code you provide since the tests are depending on the underlying database to be in a particular state. In order to isolate you need to fake or mock your data by using your own test implementations of your repositories and datacontext. You didn't specify the version of EF you're using, but from EF 6 onwards this has been made easier by having virtual DbSet properties on the DbContext which can be overridden by a mock implementation. MSDN has an article with code samples that explains this in conjunction with Moq as mocking framework. There is also a link in that article for a pre-EF6 solution.

Having said that, very often I find it too much trouble to have this clear separation in unit tests. I take the approach you seem to have taken as well by generating random entities and inserting those in a test database. Another reason for not using in memory-test doubles is that you are using Linq-to-objects instead of Linq-to-SQL which have subtle differences especially when it comes to loading related data.

To clean up your test data you can use your setup and teardown methods of the test framework you're using.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.