【问题标题】:How to unit test a repository pattern that uses Entity Framework?如何对使用实体框架的存储库模式进行单元测试?
【发布时间】:2013-04-03 15:14:45
【问题描述】:

我目前正在尝试对通过 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/stub/fakes 来进行测试。

【问题讨论】:

  • 如果您的存储库所做的只是一行代码调用 EF,那么您根本不需要对其进行任何形式的单元测试。连鲍勃大叔都同意:blog.8thlight.com/uncle-bob/2013/03/06/ThePragmaticsOfTDD.html
  • + 1 to @IronMan84 - 不要费心测试任何框架,否则您可能会发现自己在测试无法修复的代码。
  • @GertArnold 我想要发生的是在没有实际发送/连接到实际数据库的情况下测试存储库
  • @randelramirez1 你说你想要什么,但你不什么。我不知道该怎么回答。然后,你不想嘲讽,那你想伪装什么?

标签: c# entity-framework unit-testing repository-pattern


【解决方案1】:

实体框架存储库是存储库接口的具体实现。因为它是具体的,所以你不能在没有数据库的情况下对其进行抽象和测试——这个具体实现的重点就是将数据写入数据库!

这意味着测试 EF 存储库应该旨在验证存储库是否写入实际数据库 - 在测试的动作阶段调用存储库方法,在断言阶段使用任何其他方式从数据库获取数据( ado.net 也许?)检查存储库是否完成了它的工作。

另一个不相关的事情是,您可以拥有另一个使用内存数据存储的存储库实现,并将此类内存存储库注入其他服务允许您在不写入物理数据库的情况下测试这些服务。您甚至可以模拟注入到其他服务的存储库,只是为了执行一些行为测试,即测试您的服务是否正确使用您的存储库。

【讨论】:

  • +1 太多人试图模拟无法模拟的东西。我收集了一些证据here
  • @GertArnold:令人印象深刻的证据。
  • 如果我理解,您的意思是我们应该创建另一个数据访问实现(使用 ado.net)来验证 EF 是否有效。这不会增加很多开销吗?
  • @betitall:可能是这样,但您认为是否有任何其他方法可以验证 EF 存储库是否正在完成其工作?我见过人们使用同一个存储库写入数据然后读取和比较结果的测试。不幸的是,除了读取和写入之间的对应关系之外,这并没有测试任何东西。经过这样的测试,数据是否进入实际数据库仍然未知。
【解决方案2】:

您可以使用内存数据库替换与数据库的连接,例如 Effort。然后您可以测试您的存储库逻辑。更多详情可以找到here

【讨论】:

  • 我认为这应该是一个评论,而不是一个答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-19
相关资源
最近更新 更多