简体   繁体   English

首先在EF6 db中模拟数据库

[英]Mocking database in EF6 db first

I'm having trouble figuring out how to mock my database for unit testing my web api controllers. 我无法弄清楚如何模拟我的数据库以进行单元测试我的web api控制器。 All the resources I've found work for code first EF but not db first where my context is auto-generated. 我发现的所有资源都是首先用于代码的EF,而不是自动生成我的上下文的db。

Put simply I want to be able to call my controller's CRUD operations against a fake database I create on the fly and am looking for the most straight forward method of doing this. 简单地说,我希望能够针对我动态创建的假数据库调用我的控制器的CRUD操作,并且正在寻找最直接的方法。

I'm trying to use http://www.nogginbox.co.uk/blog/mocking-entity-framework-data-context to put it together but cannot manage... 我正在尝试使用http://www.nogginbox.co.uk/blog/mocking-entity-framework-data-context将它放在一起但无法管理...

My context is defined as: 我的上下文定义为:

public partial class MyEntities : DbContext
{
    public MyEntities()
        : base("name=MyEntities")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }

    public virtual DbSet<Company> Companies { get; set; }

Now I understand that I need to create an IContext which allows the mocking of MyEntities, but I cannot figure out how to organize this. 现在我明白我需要创建一个允许模拟MyEntities的IContext,但我无法弄清楚如何组织它。

I've tried adding the below classes but can't figure out how to organize it past this. 我已经尝试添加以下类,但无法弄清楚如何组织它。

public interface IContext
{
    IObjectSet<Company> Companies { get; }

    void SaveChanges();
}

public class EFContext: IContext
{
    private readonly MyEntities _data;

    public EFContext()
    {
        _data = new MyEntities();
    }

    public IObjectSet<Company> Companies
    {
        get 
        {
            return _data.CreateObjectSet<Company>();
        }
    }

    public void SaveChanges()
    {
        _data.SaveChanges();
    }
}

Example

Example controller I'd like to unit test, which would be really easy to do if I could mock a database to test against. 示例控制器我想进行单元测试,如果我可以模拟数据库进行测试,这将非常容易。

public IHttpActionResult Search([FromBody]string query)
    {
        var companies = CompanyRepository.Get().Where(c => c.Name.ToLower().Contains(query.ToLower()));
        var people = PersonRepository.Get().Where(c => c.FirstName.ToLower().Contains(query.ToLower()) || c.LastName.ToLower().Contains(query.ToLower()));

        List<SearchResult> results = new List<SearchResult>();
        foreach(Company company in companies)
            results.Add(new SearchResult { ID = company.ID, Name = company.Name, Type = "Company" });
        foreach (Person person in people)
            results.Add(new SearchResult { ID = person.ID, Name = person.FirstName + " " + person.LastName, Type = "Person" });

        return Ok(results);
    }
  1. How can I mock my EF database first context to use a test database? 如何模拟我的EF数据库第一个上下文以使用测试数据库?

Updated 11/07/2015 2015年7月11日更新

So some tests that I can see are: 所以我能看到的一些测试是:

  1. "Search should get companies once where query is not empty" “搜索应该让公司一次查询不为空”
  2. "Search should not get companies when query is empty" “当查询为空时,搜索不应该获取公司”
  3. "Search should get people once when query is not empty" “当查询不为空时,搜索应该让人们一次”
  4. "Search should not get people when query is empty" “当查询为空时,搜索不应该让人们”
  5. "Search should not add any companies when query is empty" “当查询为空时,搜索不应添加任何公司”
  6. "Search should return a result with one company if one company found from company repository" “如果从公司存储库找到一家公司,搜索应该返回一家公司的结果”
  7. "Search should return a result with two companies if two companies found from company repository" “如果两家公司从公司存储库找到,搜索应该返回两家公司的结果”
  8. "Search should not return a result with any people when query is empty" “当查询为空时,搜索不应该向任何人返回结果”
  9. "Search should return a result with one person if one person found from person repository" “如果一个人从人员库中找到,搜索应该返回一个人的结果”
  10. "Search should return a result with two people if two people found from person repository" “如果两个人从人员库中找到,搜索应返回两个人的结果”

So the first thing to think about is, "What are the dependencies" of this controller. 所以首先要考虑的是,这个控制器的“依赖性是什么”。 From this method I can see the CompanyRepository and the PersonRepository. 从这个方法我可以看到CompanyRepository和PersonRepository。 These are the things you want to mock. 这些是你想要模仿的东西。 ie you aren't testing them here or any of their functionality. 即你没有在这里测试它们或它们的任何功能。 You are testing only whats in your method. 您正在测试方法中的最新情况。

You will want to change your Controller so you can mock them eg: 您将需要更改您的控制器,以便您可以模拟它们,例如:

public class MyController : ApiController 
{
    private ICompanyRepository companyRepository;
    private IPersonRepository personRepository;

    public MyController ()
    {
        companyRepository = new CompanyRepository();
        personRepository = new PersonRepository();
    }

    public MyController (ICompanyRepository CompanyRepository, IPersonRepository PersonRepository )
    {
        companyRepository = CompanyRepository;
        personRepository = PersonRepository;
    }
}

Your code then needs to reference the private repositories 然后,您的代码需要引用私有存储库

public IHttpActionResult Search([FromBody]string query)
{
    var companies = companyRepository.Get().Where(c => c.Name.ToLower().Contains(query.ToLower()));
    var people = personRepository.Get().Where(c => c.FirstName.ToLower().Contains(query.ToLower()) || c.LastName.ToLower().Contains(query.ToLower()));

    List<SearchResult> results = new List<SearchResult>();
    foreach(Company company in companies)
        results.Add(new SearchResult { ID = company.ID, Name = company.Name, Type = "Company" });
    foreach (Person person in people)
        results.Add(new SearchResult { ID = person.ID, Name = person.FirstName + " " + person.LastName, Type = "Person" });

    return Ok(results);
}

Your tests then look something like (depending on which test & mocking framework you use). 然后你的测试看起来像(取决于你使用的测试和模拟框架)。 Please note this is quite rough coding and won't work if pasted!: 请注意这是非常粗略的编码,如果粘贴将无法工作!:

[TestClass]
public class MyControllerTests
{
    Mock<ICompanyRepository>() mockCompanyRepository = new Mock<ICompanyRepository>()
    Mock<IPersonRepository>() mockPersonRepository = new Mock<IPersonRepository>()
    MyController sut;

    Public MyControllerTests ()
    {
        // Create your "System under test" which is your MyController. Pass in your mock repositories to aid your testing.
        sut = new MyController(mockCompanyRepository.Object, mockPersonRepository.Object)
    }

    [TestMethod]
    public void SearchShouldGetCompaniesOnceWhereQueryIsNotEmpty
    {
        //Arrange
        var expectedCompanies = new List<Company>{new Company{"Company1"}, new Company{"Company2"}};
        //Setup mock that will return People list when called:
        mockCompanyRepository.Setup(x => x.Get()).Returns(expectedCompanies);
        mockPersonRepository.Setup(x => x.Get()).Returns(new List<Person>())

    //Act
    var result = sut.Search("Conway");

    //Assert - check the company repository was called once and the result was as expected
    mockCompanyRepository.Verify(x => x.Get(), Times.Once());

OkNegotiatedContentResult<string> conNegResult = Assert.IsType<OkNegotiatedContentResult<string>>(actionResult);
Assert.Equal("data: [{Name : "Company1"}, {Name : "Company2"}]", conNegResult.Content);
    }
}

You haven't included the code for your repositories in the question but if you use the above style you should be able to follow the same pattern. 您没有在问题中包含存储库的代码,但如果使用上述样式,则应该能够遵循相同的模式。 If your repository Get method has no logic in and is simply a pass through, then I would argue you don't need to write a test against it. 如果您的存储库Get方法没有逻辑并且只是一个传递,那么我认为您不需要针对它编写测试。 If you are doing funky things like ordering, filtering etc etc then I would say you do. 如果你正在做诸如订购,过滤等等时髦的事情,那么我会说你做的。 But you then test the repository by itself and in a very similar way to the above! 但是,您然后以与上述非常相似的方式自行测试存储库!

Quick note. 快速说明。 The MyController constructor has 2 methods. MyController构造函数有2个方法。 One is parameterless and the other takes your repositories. 一个是无参数的,另一个是你的存储库。 I have done this as I do not know if you are using an IoC (Inversion of Control or Dependency Injection container). 我这样做了,因为我不知道你是否使用了IoC(控制反转或依赖注入容器)。 You don't need to but it will save you having to write parameterless constructors for all the classes you want to test. 您不需要,但它将节省您必须为您要测试的所有类编写无参数构造函数。

Original Answer 原始答案

I think the first question is "What are you trying to test?". 我认为第一个问题是“你想测试什么?”。 When you write a test it should be testing just the functionality in the test target and not in any dependencies. 在编写测试时,它应该只测试测试目标中的功能而不是任何依赖项。 So arguably you shouldn't need to connect to a test database to run your unit test (unless you are doing an integration test). 因此可以说,您不需要连接到测试数据库来运行单元测试(除非您正在进行集成测试)。 You will need to mock out the EF DBContext however. 但是,您需要模拟EF DBContext。 Check this MSDN article which has plenty of examples 查看这篇包含大量示例的MSDN文章

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM