首先在EF6 db中模拟数据库



我有麻烦弄清楚如何模拟我的数据库单元测试我的web api控制器。所有的资源,我发现工作的代码首先EF,但不是db第一,我的上下文是自动生成的。

简单地说,我希望能够调用我的控制器的CRUD操作对我在飞行中创建的假数据库,我正在寻找这样做的最直接的方法。

我试图使用http://www.nogginbox.co.uk/blog/mocking-entity-framework-data-context把它放在一起,但无法管理…

我的上下文定义为:

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

现在我明白,我需要创建一个IContext,允许MyEntities的嘲弄,但我不知道如何组织这个。

我试着添加下面的类,但不知道如何组织它。

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

我想要进行单元测试的控制器示例,如果我可以模拟数据库进行测试,这将非常容易做到。

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. 我如何模拟我的EF数据库第一个上下文使用测试数据库?

更新11/07/2015

我看到的一些测试是:

  1. "搜索应该得到公司一次查询不是空的"
  2. "当查询为空时,搜索不应该得到公司"
  3. "当查询不是空的时候,搜索应该得到人们一次"
  4. "当查询为空时,搜索不应该得到人"
  5. "当查询为空时,搜索不应该添加任何公司"
  6. "如果从公司存储库中找到一家公司,则搜索应返回包含一家公司的结果"
  7. "如果从公司存储库中找到两家公司,则搜索应返回包含两家公司的结果"
  8. "当查询为空时,搜索不应该返回包含任何人员的结果"
  9. "如果从人员存储库中找到一个人,则搜索应返回包含一个人的结果"
  10. "如果从人员存储库中找到两个人,则搜索应返回包含两个人的结果"

首先要考虑的是,这个控制器的依赖关系是什么。从这个方法中我可以看到CompanyRepository和PersonRepository。这些都是你想嘲笑的东西。也就是说,你没有在这里测试它们或它们的任何功能。你只是在测试你的方法中的内容。

你会想改变你的控制器,这样你就可以模仿他们,例如:

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;
    }
}
你的代码需要引用私有存储库
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);
}

您的测试看起来像(取决于哪个测试&您使用的mock框架)。请注意,这是相当粗糙的编码,将无法工作,如果粘贴!:

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

您没有在问题中包含存储库的代码,但是如果您使用上述样式,您应该能够遵循相同的模式。如果您的存储库Get方法没有逻辑,只是一个简单的传递,那么我认为您不需要针对它编写测试。如果你在做一些奇怪的事情,比如排序,过滤等等,那么我会说你在做。但是您可以用与上面非常相似的方法单独测试存储库!

快速的注意。MyController构造函数有2个方法。一个是无参数的,另一个使用您的存储库。我已经这样做了,因为我不知道您是否正在使用IoC(控制反转或依赖注入容器)。你不需要这样做,但它会节省你为所有你想测试的类编写无参数构造函数的时间。

原始回答

我认为第一个问题是"你想测试什么?"当您编写测试时,应该只测试测试目标中的功能,而不测试任何依赖项。因此,您不需要连接到测试数据库来运行单元测试(除非您正在进行集成测试)。但是,您需要模拟EF DBContext。查看这篇MSDN文章,里面有很多例子

相关内容

  • 没有找到相关文章

最新更新