How to unit test a repository pattern that uses Entity Framework?

25,128

Solution 1

The Entity Framework repository is a concrete implementation of your repository interface. Because it is concrete, you can't abstract it and test without the database - the whole point of this concrete implementation is to write the data to the database!

This means that testing EF repositories should rather aim at verifying that repositories write to actual database - in the act phase of your test you invoke repository methods and in the assert phase you use any other way of getting the data from the database (ado.net perhaps?) to check if the repository does its job.

Yet another, unrelated thing is that you can have another implementation of the repository which uses an in-memory data store and injecting such in-memory repository to other services allows you to test these services wihout writing to a physical database. You could even mock a repository injected to other services just to perform some behavioral tests, i.e. test whether your services use your repositories correctly.

Solution 2

You can replace connection to your database with in-memory DB, such as Effort. Then you can test your repository logic. In more details can be found here

Share:
25,128

Related videos on Youtube

Randel Ramirez
Author by

Randel Ramirez

{ C# | ASP.NET Core | Entity Framework Core | JavaScript | TypeScript | Web Applications | Mobile Applications | Xamarin | OOP } Get it to work. Make the code beautiful. Optimize.

Updated on July 28, 2020

Comments

  • Randel Ramirez
    Randel Ramirez almost 4 years

    I'm currently trying to unit test a repository I made through Entity Framework:

    What I want to happen is that test the repository without actually sending/connecting to the actual database, I want to this without using any mocking framework.

    Currently my test is sending the data to the database, what I want to happen is test the add/remove etc. methods without sending the actual data to the database since it's only for testing.

    Here is the repository:

    namespace AbstractFactory.Repository
    {
        using System.Collections.Generic;
        using System.Data.Entity;
        using System.Linq;
    
        /// <summary>
        /// This class serves as the structure of the Author repository using a database
        /// </summary>
        public class DbAuthorRepository : IRepository<AuthorEntity>
        {
    
            private AbstractFactoryPatternEntities context;
    
            public DbAuthorRepository(AbstractFactoryPatternEntities context)
            {
                this.context = context;
            }
    
            /// <summary>
            /// Adds the specified author.
            /// </summary>
            /// <param name="author">The author.</param>
            public void Add(AuthorEntity author)
            {
                context.AuthorEntities.Add(author);
            }
    
            /// <summary>
            /// Removes the specified author.
            /// </summary>
            /// <param name="author">The author.</param>
            public void Remove(AuthorEntity author)
            {
                this.context.AuthorEntities.Remove(author);
            }
    
            /// <summary>
            /// Saves this instance.
            /// </summary>
            public void Save()
            {
                this.context.SaveChanges();
            }
    
            /// <summary>
            /// Gets all.
            /// </summary>
            /// <returns>returns a list of all the authors</returns>
            public IEnumerable<AuthorEntity> GetAll()
            {
                List<AuthorEntity> result = this.context.AuthorEntities.Include(a => a.Books).ToList();
    
                return result;
            }
    
            /// <summary>
            /// Gets the author by id.
            /// </summary>
            /// <param name="id">The id.</param>
            /// <returns>returns an entity</returns>
            public AuthorEntity GetById(int id)
            {
                AuthorEntity result = this.context.AuthorEntities.Single(a => a.AuthorId == id);
    
                return result;
            }
        }
    }
    

    Here is the current code for the unit test:

    [TestMethod]
            public void Add_MethodIsCalled_EntityCountIsIncrementedByOne()
            {
                using (ShimsContext.Create())
                {
                    ShimAbstractFactoryPatternEntities context = new ShimAbstractFactoryPatternEntities(new AbstractFactoryPatternEntities());
                    DbAuthorRepository repository = new DbAuthorRepository(context);
                    repository.Add(new AuthorEntity { FirstName = "Test", LastName = "testing=" });
                    var actual = repository.GetAll().Count();
                    repository.Save();
                    var expected = repository.GetAll().Count();
                    Assert.AreNotEqual(actual, expected);
                }
    
                //AbstractFactoryPatternEntities context = new AbstractFactoryPatternEntities();
                //DbAuthorRepository repository = new DbAuthorRepository(context);
                //var actual = repository.GetAll().Count();
                //repository.Add(new AuthorEntity { FirstName = "Testing", LastName = "MyTest" });
                //repository.Save();
                //var expected = repository.GetAll().Count();
                //Assert.AreNotEqual(actual, expected);
            }
    
            [TestMethod]
            public void Remove_MethodIsCalled_EntityCountRemainsTheSame()
            {
                AbstractFactoryPatternEntities context = new AbstractFactoryPatternEntities();
                DbAuthorRepository repository = new DbAuthorRepository(context);
                AuthorEntity newAuthor = new AuthorEntity { FirstName = "Testing", LastName = "MyTest" };
                repository.Add(newAuthor);
                repository.Save();
                var actual = repository.GetAll().Count();
                Console.WriteLine(actual);
                repository.Remove(newAuthor);
                var expected = repository.GetAll().Count();
                Console.WriteLine(expected);
                Assert.AreEqual(actual, expected);
            }
    
            [TestMethod]
            public void Get_MethodIsCalled_CorrectAuthorIsRetrieved()
            {
                AbstractFactoryPatternEntities context = new AbstractFactoryPatternEntities();
                DbAuthorRepository repository = new DbAuthorRepository(context);
                int target = 4;
                AuthorEntity author = repository.GetById(target);
                Assert.AreEqual(target, author.AuthorId);
            }
    

    I want to use shims/stub/fakes in order to make the test.

    • Corey Adler
      Corey Adler about 11 years
      If all that your repositories are doing is one line of code that is making a call to EF then you don't need to do any form of unit testing at all on it. Even Uncle Bob agrees: blog.8thlight.com/uncle-bob/2013/03/06/ThePragmaticsOfTDD.ht‌​ml
    • Srikanth Venugopalan
      Srikanth Venugopalan about 11 years
      + 1 to @IronMan84 - dont bother testing any framework, else you could potentially find yourself testing code that you can't fix.
    • Randel Ramirez
      Randel Ramirez about 11 years
      @GertArnold What I want to happen is that test the repository without actually sending/connecting to the actual database
    • Gert Arnold
      Gert Arnold about 11 years
      @randelramirez1 You tell what you want, but you don't ask anything. I don't know what to answer. And then, you don't want to mock, so what do you want to fake?
  • Gert Arnold
    Gert Arnold about 11 years
    +1 Too many people try to mock something that can't be mocked. I collected some evidence here.
  • Wiktor Zychla
    Wiktor Zychla about 11 years
    @GertArnold: impressive evidence.
  • Bento Set
    Bento Set over 10 years
    If I understand, you're saying that we should create another data access implementation (using ado.net) to verify that EF works. Doesn't this add a lot of overhead?
  • Wiktor Zychla
    Wiktor Zychla over 10 years
    @betitall: it probably is but is there in your opinion any other way to verify that the EF repository is doing its job? I've seen tests where people use the same repository to write data and then read and compare results. This doesn't unfortunately test anything other than the correspondence between reads and writes. It is still unknown after such test whether the data go to actual database or not.
  • Iman Mahmoudinasab
    Iman Mahmoudinasab over 9 years
    I think this should be a comment and it is not an answer.