如何对使用Entity Framework的仓储模式进行单元测试?

18

我目前正在尝试通过Entity Framework对一个仓库进行单元测试:

我希望的是,测试仓库时不实际发送/连接到实际数据库,而且不使用任何模拟框架。

目前我的测试将数据发送到数据库中,我想要的是测试添加/删除等方法,而无需将实际数据发送到数据库中,因为这只是为了测试。

这是仓库:

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;
        }
    }
}

这是当前单元测试的代码:

[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);
        }

我想使用 "shims/stubs/fakes" 来进行测试。


1
如果你的所有存储库都只是执行一行代码,即调用EF,则根本不需要对其进行任何形式的单元测试。即使是Bob大叔也同意这一点:http://blog.8thlight.com/uncle-bob/2013/03/06/ThePragmaticsOfTDD.html - Corey Adler
给 @IronMan84 加 1 - 不要测任何框架,否则你可能会发现自己在测试无法修复的代码。 - Srikanth Venugopalan
@GertArnold 我想要的是在不实际发送/连接到实际数据库的情况下测试存储库。 - Randel Ramirez
@randelramirez1 你告诉我你想要什么,但你没有“问”任何问题。我不知道该怎么回答。然后,你也不想嘲笑,那么你想要伪造什么? - Gert Arnold
2个回答

22

Entity Framework repository是您存储库接口的具体实现。由于它是具体的,因此您无法在没有数据库的情况下使用抽象化进行测试 -这种具体实现的整个重点是将数据写入数据库!

这意味着测试EF存储库应该更多地关注验证存储库是否写入了实际的数据库 - 在测试的执行阶段中,您调用存储库方法,在断言阶段中,您可以使用任何其他方式从数据库获取数据(例如ado.net)来检查存储库是否完成其工作。

另一个不相关的事情是,您可以拥有另一种实现存储库的方式,该实现使用内存数据存储库,并将这种内存数据存储库注入到其他服务中,以便测试这些服务而无需写入物理数据库。您甚至可以模拟注入到其他服务中的存储库,只是为了执行一些行为测试,即测试您的服务是否正确使用您的存储库。


7
+1有太多人试图嘲弄那些不能被嘲弄的东西。我在这里收集了一些证据(链接:https://dev59.com/jWYr5IYBdhLWcg3w4t3m#13352779)。 - Gert Arnold
@GertArnold:令人印象深刻的证据。 - Wiktor Zychla
1
如果我理解正确,您是在说我们应该创建另一个数据访问实现(使用ado.net)来验证EF是否起作用。这难道不会增加很多开销吗? - betitall
@betitall:可能是这样,但您认为还有其他验证EF存储库是否正常工作的方法吗?我看过一些测试,人们使用相同的存储库来编写数据,然后读取和比较结果。不幸的是,这并没有测试除读写之间的对应关系以外的任何内容。在这样的测试之后,仍然不知道数据是否实际进入了数据库。 - Wiktor Zychla

8

您可以将连接到数据库的方式更改为内存数据库,例如Effort。然后,您可以测试您的存储库逻辑。更多详细信息,请参见此处


3
我认为这应该是一条评论,而不是答案。 - Iman Mahmoudinasab

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接