假的DbContext of Entity Framework 4.1来测试

 2023-02-17    397  

问题描述

我正在使用本教程来伪造我的dbcontext和测试: http://refactorthis.wordpress.com/2011/05/31/mock-faking-dbcontext-in-entity-framework-4-1-带 – a-generic-repository/

但我必须更改fakemainmodulecontext的实现以在我的控制器中使用:

假的DbContext of Entity Framework 4.1来测试

public class FakeQuestiona2011Context : IQuestiona2011Context
{
    private IDbSet<Credencial> _credencial;
    private IDbSet<Perfil> _perfil;
    private IDbSet<Apurador> _apurador;
    private IDbSet<Entrevistado> _entrevistado;
    private IDbSet<Setor> _setor;
    private IDbSet<Secretaria> _secretaria;
    private IDbSet<Pesquisa> _pesquisa;
    private IDbSet<Pergunta> _pergunta;
    private IDbSet<Resposta> _resposta;

    public IDbSet<Credencial> Credencial { get { return _credencial ?? (_credencial = new FakeDbSet<Credencial>()); } set { } }
    public IDbSet<Perfil> Perfil { get { return _perfil ?? (_perfil = new FakeDbSet<Perfil>()); } set { } }
    public IDbSet<Apurador> Apurador { get { return _apurador ?? (_apurador = new FakeDbSet<Apurador>()); } set { } }
    public IDbSet<Entrevistado> Entrevistado { get { return _entrevistado ?? (_entrevistado = new FakeDbSet<Entrevistado>()); } set { } }
    public IDbSet<Setor> Setor { get { return _setor ?? (_setor = new FakeDbSet<Setor>()); } set { } }
    public IDbSet<Secretaria> Secretaria { get { return _secretaria ?? (_secretaria = new FakeDbSet<Secretaria>()); } set { } }
    public IDbSet<Pesquisa> Pesquisa { get { return _pesquisa ?? (_pesquisa = new FakeDbSet<Pesquisa>()); } set { } }
    public IDbSet<Pergunta> Pergunta { get { return _pergunta ?? (_pergunta = new FakeDbSet<Pergunta>()); } set { } }
    public IDbSet<Resposta> Resposta { get { return _resposta ?? (_resposta = new FakeDbSet<Resposta>()); } set { } }

    public void SaveChanges()
    {
        // do nothing (probably set a variable as saved for testing)
    }
}

和我的测试:

[TestMethod]
public void IndexTest()
{
    IQuestiona2011Context fakeContext = new FakeQuestiona2011Context();
    var mockAuthenticationService = new Mock<IAuthenticationService>();

    var apuradores = new List<Apurador>
    {
        new Apurador() { Matricula = "1234", Nome = "Acaz Souza Pereira", Email = "acaz@telecom.inf.br", Ramal = "1234" },
        new Apurador() { Matricula = "4321", Nome = "Samla Souza Pereira", Email = "samla@telecom.inf.br", Ramal = "4321" },
        new Apurador() { Matricula = "4213", Nome = "Valderli Souza Pereira", Email = "valderli@telecom.inf.br", Ramal = "4213" }
    };
    apuradores.ForEach(apurador => fakeContext.Apurador.Add(apurador));

    ApuradorController apuradorController = new ApuradorController(fakeContext, mockAuthenticationService.Object);
    ActionResult actionResult = apuradorController.Index();

    Assert.IsNotNull(actionResult);
    Assert.IsInstanceOfType(actionResult, typeof(ViewResult));

    ViewResult viewResult = (ViewResult)actionResult;

    Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(IndexViewModel));

    IndexViewModel indexViewModel = (IndexViewModel)viewResult.ViewData.Model;

    Assert.AreEqual(3, indexViewModel.Apuradores.Count);
}

我正在做对吗?

不幸的是,你不是正确的,因为这篇文章是错误的.它假装FakeContext将使您的代码单元可测试,但它不会.一旦您将IDbSet或IQueryable曝光到您的控制器并且您将集合带到内存集合中,您就无法确定您的单元测试真正测试代码.在控制器中编写LINQ查询非常容易,它将通过您的单元测试(因为FakeContext使用LINQ-Objects)但在运行时失败(因为您的真实上下文使用LINQ-ENTITES).这使得您的单位测试无用的全部目的.

我的意见:如果要将设置暴露给Controller,请不要打扰伪装语境.而是使用具有真实数据库进行测试的集成测试.这是如何验证控制器中定义的LINQ查询的唯一方法,请执行您所期望的内容.

肯定,如果您想在您的集合中拨打ToList或FirstOrDefault,则为FakeContext将适合您,但一旦您做得更复杂,您可以很快找到一个陷阱(只需将字符串“不能被翻译成商店表达式”进入谷歌 – 只有在运行linq到实体时才会出现所有这些问题,但它们会将测试与linq-to-objects传递).

这是一个非常常见的问题,以便您可以查看其他示例:

  • 返回iqueryable或不返回iqueryable
  • 单元测试dbcontext
  • ASP.NET MVC3和实体框架代码第一架构
  • 组织,我应该在使用实体框架代码时将常见的查询?
  • 是可以测试数据访问层的存根实体框架上下文和类?

其他推荐答案

“不幸的是,你不是正确的,因为这篇文章是错误的.它假装fakecontext将使您的代码单元可测试,但它不会”

我是你所指的博客文章的创造者.我在这里看到的问题是对N层单元测试的基本原理的误解.我的帖子并不意味着直接用于测试控制器逻辑.

单位测试应该完全按照名称暗示和测试”一个单位”.如果我测试了一个控制器(如上所述)我忘记了关于数据访问的所有信息.我应该在我的脑海中删除对数据库上下文的所有呼叫,并用黑色盒子方法调用替换它们,就像这些操作对我未知一样.这是我对测试感兴趣的那些操作的代码.

示例:

在我的mvc应用程序中,我们使用存储库模式.我有一个存储库,说CustomErrepository:iCustomErrepository,它将执行我的所有客户数据库操作.

如果我要测试我的控制器,我希望测试测试我的存储库,我的数据库访问和控制器逻辑本身吗?当然不是! 在这个管道中有很多”单位”.您想要做的是创建一个虚假的存储库,它实现了iCustomErreposory,允许您以隔离测试控制器逻辑.

这是我所知的最佳知识,不能单独在数据库上下文中完成. (除了使用Microsoft Moles,您可以查看您是否愿意的话).这只是因为所有查询都在控制器类中的上下文之外执行.

如果我想测试CustomErrepository逻辑,我该怎么办?最简单的方法是使用假上下文.这将允许我确保当我试图按ID获取客户时,它实际上可以通过ID等待客户.存储库方法非常简单,”无法转换为商店表达式”问题通常不会表面.虽然在某些次要情况下,它可能(有时由于LINQ查询错误地编写了LINQ查询),但还要执行将代码一直测试到数据库的集成测试是很重要的.这些问题将在集成测试中找到.我已经使用了这个n层技术,有很长一段时间,并且没有发现这个问题.

集成测试

显然,对数据库本身测试您的应用程序是一种昂贵的运动,一旦您获得成千上万的测试,它就会成为一个噩梦,另一方面最好模仿代码如何在”现实世界”中.这些测试很重要(从UI到数据库)也是如此,它们将作为集成测试的一部分进行,而不是单位测试.

acaz,您在场景中真正需要的是一种咀嚼/可粘贴的存储库.如果您希望根据您正在进行测试控制器,那么您的控制器应该采用包裹数据库功能的对象.然后它可以返回您需要它的任何内容,以便测试控制器功能的所有方面.

看 http://msdn.microsoft.com/en-us/library/ff714955.aspx

为了测试存储库本身(如果需要在所有情况下辩论),您将想伪造上下文或沿着”Moles”框架的行.

LINQ本身难以测试.使用扩展方法在上下文之外定义查询的事实使我们提供了极大的灵活性,但创造了一个测试噩梦.将上下文包装在存储库中,此问题消失.

抱歉这么长:)

其他推荐答案

作为Ladislav mrnka提到的,您应该测试LINQ-ENTITY但不是LINQ-Object.我通常使用SQL CE作为测试数据库,并且始终在每次测试之前重新创建数据库.这可能会使测试有点慢但到目前为止,我可以对我的100多个单元测试的性能进行.

首先,在测试项目的 app.config 中使用sqlce更改连接字符串设置.

<connectionStrings>
    <add name="MyDbContext"
       connectionString="Data Source=|DataDirectory|MyDb.sdf"
         providerName="System.Data.SqlServerCe.4.0"
         />
</connectionStrings>

第二,使用 dropcreatedatabasealways设置db初始化程序.

Database.SetInitializer<MyDbContext>(new DropCreateDatabaseAlways<MyDbContext>());

然后,在运行每个测试之前强制EF初始化.

public void Setup() {
    Database.SetInitializer<MyDbContext>(new DropCreateDatabaseAlways<MyDbContext>());
    context = new MyDbContext();
    context.Database.Initialize(force: true);
}

如果使用xuenit,请在构造函数中调用安装方法.如果您使用的是MSTEST,请将TestInitializeattribute放在该方法上.如果nunit …….

以上所述是小编给大家介绍的假的DbContext of Entity Framework 4.1来测试,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对77isp云服务器技术网的支持!

原文链接:https://77isp.com/post/34209.html

=========================================

https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。