如何与实体框架6人的单元测试,你应该这么做呢? [英] How are people unit testing with Entity Framework 6, should you bother?

查看:270
本文介绍了如何与实体框架6人的单元测试,你应该这么做呢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚开始接触单元testings和TDD一般。我以前涉足,但现在我决心将它添加到我的工作流程,写更好的软件。

I am just starting out with Unit testings and TDD in general. I have dabbled before but now I am determined to add it to my workflow and write better software.

昨天我问了一个问题之类的,这包括,但它似乎是对自己的一个问题。我已经坐了下来开始实施,我会用抽象掉了业务逻辑控制器和映射到使用EF6具体型号和数据交互的服务类。

I asked a question yesterday that kind of included this, but it seems to be a question on its own. I have sat down to start implementing a service class that I will use to abstract away the business logic from the controllers and map to specific models and data interactions using EF6.

问题是我自己包版广告已经因为我不想抽象EF走在储存库(它仍然可用于特定查询等服务外),并想测试我的服务(EF上下文会使用)。

The issue is I have roadblocked myself already because I didn't want to abstract EF away in a repository (it will still be available outside the services for specific queries, etc) and would like to test my services (EF Context will be used).

下面我想是问题,有没有点这样做呢?如果是这样,如何​​通过人拉吉斯拉夫Mrnka做它在野外造成的IQueryable和许多伟大的帖子漏抽象的光的关于在内存实现与作业时,结合在一个特定的数据库不是因为在LINQ提供的差异是简单的单元测试的主题。

Here I guess is the question, is there a point to doing this? If so, how are people doing it in the wild in light of the leaky abstractions caused by IQueryable and the many great posts by Ladislav Mrnka on the subject of unit testing not being straightforward because of the differences in Linq providers when working with an in memory implementation as apposed to a specific database.

在code我想测试似乎pretty简单。 (这只是假code,试图了解我在做什么,我想开车使用TDD创建)

The code I want to test seems pretty simple. (this is just dummy code to try and understand what i am doing, I want to drive the creation using TDD)

上下文

public interface IContext
{
    IDbSet<Product> Products { get; set; }
    IDbSet<Category> Categories { get; set; }
    int SaveChanges();
}

public class DataContext : DbContext, IContext
{
    public IDbSet<Product> Products { get; set; }
    public IDbSet<Category> Categories { get; set; }

    public DataContext(string connectionString)
                : base(connectionString)
    {

    }
}

服务

public class ProductService : IProductService
{
    private IContext _context;

    public ProductService(IContext dbContext)
    {
        _context = dbContext;
    }

    public IEnumerable<Product> GetAll()
    {
        var query = from p in _context.Products
                    select p;

        return query;
    }
}

目前我在做一些事情的心态:

Currently I am in the mindset of doing a few things:


  1. 嘲讽EF上下文像这样approach- <一个href=\"http://www.asp.net/web-api/overview/testing-and-debugging/mocking-entity-framework-when-unit-testing-aspnet-web-api-2\">Mocking EF当单元测试或直接使用最小起订量一样在接口上模拟框架 - 以该单元测试可能会通过,但不一定工作,端到端的,并将其备份与集成测试痛

  2. 也许使用像努力以模拟EF - 我从来没有使用它,如果任何人在使用它不知道在野外?

  3. 理会测试什么,只是简单地调用回EF - ?调用EF直接(GETALL等),所以基本上服务方法都没有了单元测试只是测试集成

  1. Mocking EF Context with something like this approach- Mocking EF When Unit Testing or directly using a mocking framework on the interface like moq - taking the pain that the unit tests may pass but not necessarily work end to end and back them up with Integration tests?
  2. Maybe using something like Effort to mock EF - I have never used it and not sure if anyone else is using it in the wild?
  3. Not bother testing anything that simply calls back to EF - so essentially service methods that call EF directly (getAll etc) are not unit tested but just integration tested?

任何人在那里实际上这样做在那里没有一个回购和具有成功?

Anyone out there actually doing this out there without a Repo and having success?

推荐答案

这是一个话题我在。很感兴趣有很多较真谁说,你不应该测试技术,如EF和NHibernate。他们是正确的,他们已经非常严格测试,并为previous回答说,它往往没有意义花费大量的时间进行测试,你没有自己的东西。

This is a topic I'm very interested in. There are many purists who say that you shouldn't test technologies such as EF and NHibernate. They are right, they're already very stringently tested and as a previous answer stated it's often pointless to spend vast amounts of time testing what you don't own.

但是,你自己下的数据库!这是这种做法在我看来,坏了,你不需要测试EF / NH正确地做他们的工作。你需要测试你的映射/实现与数据库的工作。在我看来这是可以测试一个系统中最重要的部分之一。

However, you do own the database underneath! This is where this approach in my opinion breaks down, you don't need to test that EF/NH are doing their jobs correctly. You need to test that your mappings/implementations are working with your database. In my opinion this is one of the most important parts of a system you can test.

严格地说但是我们正在走出单元测试的领域,进入集成测试,但本金保持不变。

Strictly speaking however we're moving out of the domain of unit testing and into integration testing but the principals remain the same.

您需要做的第一件事就是要能够嘲笑你的DAL让你BLL可以独立EF和SQL进行测试。 这是你的单元测试。下一步你需要设计你的集成测试以证明你的DAL,在我看来,这是每一位同样重要。

The first thing you need to do is to be able to mock your DAL so your BLL can be tested independently of EF and SQL. These are your unit tests. Next you need to design your Integration Tests to prove your DAL, in my opinion these are every bit as important.

有几件事情要考虑:


  1. 您的数据库需要与每个测试已知状态。大多数系统使用一个备份或为此创建脚本。

  2. 每次测试必须是可重复

  3. 每次测试必须是原子

有来设置数据库两个主要方法是,首先是要运行的UnitTest创建数据库的脚本。这可以确保你的单元测试数据库将始终在每次测试开始时相同的状态(您既可以重置此或在事务中运行每个测试,以确保这一点)。

There are two main approaches to setting up your database, the first is to run a UnitTest create DB script. This ensures that your unit test database will always be in the same state at the beginning of each test (you may either reset this or run each test in a transaction to ensure this).

您另一种选择是我做的,运行每个测试的具体设置。我相信这是两个主要的原因,最好的方法:

Your other option is what I do, run specific setups for each individual test. I believe this is the best approach for two main reasons:


  • 您的数据库比较简单,你不需要整个模式为每个测试

  • 每个测试是安全的,如果你在创建脚本改变一个值时,它并不否定其他几十个测试。

很遗憾,您在这里的妥协是速度。这需要时间来运行所有这些测试,运行所有这些安装/拆除的脚本。

Unfortunately your compromise here is speed. It takes time to run all these tests, to run all these setup/tear down scripts.

最后一点,它可以是非常艰苦的工作写出如此大量的SQL来测试你的ORM。这是我采取非常讨厌的方法(这里的纯粹主义者会不同意我)。我用我的ORM创建我的测试!而不是在我的系统中每个DAL测试一个单独的脚本我有一个创建的对象,他们重视的背景下,拯救他们的测试安装阶段。然后,我跑我的测试。

One final point, it can be very hard work to write such a large amount of SQL to test your ORM. This is where I take a very nasty approach (the purists here will disagree with me). I use my ORM to create my test! Rather than having a separate script for every DAL test in my system I have a test setup phase which creates the objects, attaches them to the context and saves them. I then run my test.

这是远非理想的解决方案然而在实践中,我觉得这是一个更容易管理(特别是当你有几千个测试),否则你创建脚本的庞大的数字。在实用性纯度。

This is far from the ideal solution however in practice I find it's a LOT easier to manage (especially when you have several thousand tests), otherwise you're creating massive numbers of scripts. Practicality over purity.

我无疑会回头看看这个答案在数年(月/日),并与自己不同意我的做法已经改变 - 不过这是我目前的做法

I will no doubt look back at this answer in a few years (months/days) and disagree with myself as my approaches have changed - however this is my current approach.

要尝试和总结的一切我上面说这是我的典型DB集成测试:

To try and sum up everything I've said above this is my typical DB integration test:

[Test]
public void LoadUser()
{
  this.RunTest(session => // the NH/EF session to attach the objects to
  {
    var user = new UserAccount("Mr", "Joe", "Bloggs");
    session.Save(user);
    return user.UserID;
  }, id => // the ID of the entity we need to load
  {
     var user = LoadMyUser(id); // load the entity
     Assert.AreEqual("Mr", user.Title); // test your properties
     Assert.AreEqual("Joe", user.Firstname);
     Assert.AreEqual("Bloggs", user.Lastname);
  }
}

这里要注意的关键一点是,两个循环的会议是完全独立的。在您的实现的runTest你必须确保环境承诺和破坏,数据只能从你的数据库中的第二部分。

The key thing to notice here is that the sessions of the two loops are completely independent. In your implementation of RunTest you must ensure that the context is committed and destroyed and your data can only come from your database for the second part.

修改13/10/2014

我没有说我可能会在未来几个月修改这一模式。虽然我主要是由方法站在我上面的主张,我稍微更新了我的测试机制。我现在倾向于在TestSetup和TestTearDown创建的实体。

I did say that I'd probably revise this model over the upcoming months. While I largely stand by the approach I advocated above I've updated my testing mechanism slightly. I now tend to create the entities in in the TestSetup and TestTearDown.

[SetUp]
public void Setup()
{
  this.SetupTest(session => // the NH/EF session to attach the objects to
  {
    var user = new UserAccount("Mr", "Joe", "Bloggs");
    session.Save(user);
    this.UserID =  user.UserID;
  });
}

[TearDown]
public void TearDown()
{
   this.TearDownDatabase();
}

然后分别测试每个属性。

Then test each property individually

[Test]
public void TestTitle()
{
     var user = LoadMyUser(this.UserID); // load the entity
     Assert.AreEqual("Mr", user.Title);
}

[Test]
public void TestFirstname()
{
     var user = LoadMyUser(this.UserID);
     Assert.AreEqual("Joe", user.Firstname);
}

[Test]
public void TestLastname()
{
     var user = LoadMyUser(this.UserID);
     Assert.AreEqual("Bloggs", user.Lastname);
}

有几个原因这种方法


  • 有没有额外的数据库调用(一个安装中,拆除)

  • 测试是更加精细,每个测试验证一个属性

  • 设置/拆除逻辑从试验方法去除自己

我觉得这使测试类简单的测试更精细的(<一个href=\"http://programmers.stackexchange.com/questions/7823/is-it-ok-to-have-multiple-asserts-in-a-single-unit-test\">single声称是好的)

I feel this makes the test class simpler and the tests more granular (single asserts are good)

修改2015年5月3日

这是这种方法的另一个版本。而一流水平的设置对于测试非常有用,如装载性能他们在哪里都需要不同的设置不太有用。在这种情况下设置为每一种情况下一个新的类是矫枉过正。

Another revision on this approach. While class level setups are very helpful for tests such as loading properties they are less useful where the different setups are required. In this case setting up a new class for each case is overkill.

为了解决这个问题我现在倾向于有两个基类 SetupPerTest SingleSetup 。这两个类的要求暴露的框架。

To help with this I now tend to have two base classes SetupPerTest and SingleSetup. These two classes expose the framework as required.

SingleSetup 我们有一个非常类似的机制,在我的第一个编辑描述。一个例子是

In the SingleSetup we have a very similar mechanism as described in my first edit. An example would be

public TestProperties : SingleSetup
{
  public int UserID {get;set;}

  public override DoSetup(ISession session)
  {
    var user = new User("Joe", "Bloggs");
    session.Save(user);
    this.UserID = user.UserID;
  }

  [Test]
  public void TestLastname()
  {
     var user = LoadMyUser(this.UserID); // load the entity
     Assert.AreEqual("Bloggs", user.Lastname);
  }

  [Test]
  public void TestFirstname()
  {
       var user = LoadMyUser(this.UserID);
       Assert.AreEqual("Joe", user.Firstname);
  }
}

然而这确保只有正确的entites加载可使用SetupPerTest方法引用

However references which ensure that only the correct entites are loaded may use a SetupPerTest approach

public TestProperties : SetupPerTest
{
   [Test]
   public void EnsureCorrectReferenceIsLoaded()
   {
      int friendID = 0;
      this.RunTest(session =>
      {
         var user = CreateUserWithFriend();
         session.Save(user);
         friendID = user.Friends.Single().FriendID;
      } () =>
      {
         var user = GetUser();
         Assert.AreEqual(friendID, user.Friends.Single().FriendID);
      });
   }
   [Test]
   public void EnsureOnlyCorrectFriendsAreLoaded()
   {
      int userID = 0;
      this.RunTest(session =>
      {
         var user = CreateUserWithFriends(2);
         var user2 = CreateUserWithFriends(5);
         session.Save(user);
         session.Save(user2);
         userID = user.UserID;
      } () =>
      {
         var user = GetUser(userID);
         Assert.AreEqual(2, user.Friends.Count());
      });
   }
}

在总结都取决于你试图测试方法是什么工作。

In summary both approaches work depending on what you are trying to test.

这篇关于如何与实体框架6人的单元测试,你应该这么做呢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆