为了账号安全,请及时绑定邮箱和手机立即绑定

实体框架4.1的假DbContext测试

/ 猿问

实体框架4.1的假DbContext测试

莫回无 2019-10-20 16:12:17

实体框架4.1的假DbContext测试

我使用本教程来伪造我的DbContext和测试:http:/refactorthis.wordpress.com/2011/05/31/模拟-伪造-dbtext-in-framework-4-1-with-a-泛型-存储库/

但是,我必须将FakeMainModuleContext实现更改为在我的控制器中使用:

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


我做得对吗?


查看完整描述

3 回答

?
慕仰8121524

不幸的是,你做得不对,因为那篇文章是错的。它假装FakeContext将使您的代码单元可测试,但不会。一旦你暴露IDbSetIQueryable对于您的控制器和您在内存集合中伪造的集,您永远无法确定单元测试是否真的测试了您的代码。在控制器中编写LINQ查询非常容易,这将通过单元测试(因为FakeContext使用LINQto-Objects),但在运行时失败(因为您的实际上下文使用LINQto-实体)。这使得你的单元测试的全部目的变得毫无用处。

我的观点是:如果你想把设置暴露给控制器,就不要去理会伪造的上下文。相反,使用集成测试和真正的数据库进行测试。只有这样,才能验证控制器中定义的LINQ查询是否能满足您的期望。

当然,如果你想打电话ToListFirstOrDefault在你们的布景上FakeContext会很好地为你服务,但是一旦你做了更复杂的事情,你很快就能找到一个陷阱(只要把绳子放上去就行“无法转换为存储表达式”在google中,所有这些问题只有在您从linq到实体运行时才会出现,但它们将通过Linq到对象的测试)。

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

  • 返回IQueryable或不返回IQueryable

  • 单元测试

  • ASP.NETMVC 3与实体框架代码第一体系结构

  • 在组织上,当首先使用实体框架代码时,我应该把常见的查询放在哪里?

  • 可以通过存根实体框架上下文和类来测试数据访问层吗?



查看完整回答
反对 回复 2019-10-21
?
慕容3067478

“不幸的是,您没有正确地做这件事,因为这篇文章是错误的。它假装FakeContext会使您的代码单元可测试,但它不会。”

我是你所提到的博客文章的创作者。我在这里看到的问题是对N层单元测试基本原理的误解。我的文章不打算直接用于测试控制器逻辑。

单元测试应该按照名称所暗示的那样进行,并测试“一个单元”。如果我正在测试一个控制器(正如您在上面所做的那样),我将忘记所有关于数据访问的内容。我应该在脑海中删除对数据库上下文的所有调用,并将它们替换为黑匣子方法调用,就好像这些操作对我来说是未知的一样。我感兴趣的是围绕这些操作的代码。

例子:

在我的MVC应用程序中,我们使用存储库模式。我有一个存储库,比如CustomerRepository:ICustuerRepository,它将执行我所有的客户数据库操作。

如果我要测试我的控制器,我会希望测试来测试我的存储库,我的数据库访问,以及控制器逻辑本身吗?当然不是!这里有很多“单位”。您想要做的是创建一个伪存储库,它实现了ICustuerRepository,允许您隔离地测试控制器逻辑。

据我所知,这不能仅在数据库上下文上完成。(除了使用MicrosoftMoles之外,如果您愿意,可以查看它)。这只是因为所有查询都是在控制器类的上下文之外执行的。

如果我想测试CustomerRepository逻辑,我该怎么做?最简单的方法是使用假上下文。这将使我能够确保,当我试图通过id获取一个客户时,它实际上是通过id获得客户的,依此类推。存储库方法非常简单,“无法转换为存储表达式”的问题通常不会浮出水面。虽然在一些次要的情况下,它可能(有时由于写错了Linq查询),但在这些情况下,还必须执行集成测试,这些测试将测试您的代码直到数据库。在集成测试中会发现这些问题。我使用这个N层技术已经有一段时间了,并且没有发现任何问题。

整合测试

显然,在数据库中测试应用程序本身是一项代价高昂的工作,一旦您进行了数以万计的测试,它就变成了一场噩梦,另一方面,它最好地模拟代码将如何在“真实世界”中使用。这些测试也很重要(从UI到数据库),它们将作为集成测试的一部分执行,而不是单元测试。

Acaz,您在场景中真正需要的是一个可模拟/可伪造的存储库。如果您希望像您正在做的那样测试您的控制器,那么您的控制器应该接收一个封装数据库功能的对象。然后,它可以返回您需要它的任何东西,以便测试控制器功能的所有方面。

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

为了测试存储库本身(在所有情况下都需要进行辩论),您将需要伪造上下文或使用类似于“Moles”框架的内容。

Linq天生就很难测试。使用扩展方法在上下文之外定义查询这一事实为我们提供了极大的灵活性,但却造成了测试噩梦。将上下文封装到存储库中,这个问题就会消失。

对不起:)



查看完整回答
反对 回复 2019-10-21
?
撒科打诨

正如LadislavMrnka所提到的,您应该测试Linq到实体,而不是Linq到Object。我通常使用SQLCE作为测试DB,并且总是在每次测试之前重新创建数据库。这可能会使测试有点慢,但到目前为止,我对我的100+单元测试的性能还好。

首先,用Sqlce在App.config你的测试项目。

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

第二,将db初始化器设置为DropCreateDatabaseAlways.

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

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

public void Setup() {
    Database.SetInitializer<MyDbContext>(new DropCreateDatabaseAlways<MyDbContext>());

    context = new MyDbContext();
    context.Database.Initialize(force: true);}

如果使用xunit,请在构造函数中调用安装方法。如果您正在使用MSTest,则将TestInitializeAttribute放在该方法上。如果不是单位.。



查看完整回答
反对 回复 2019-10-21
  • 3 回答
  • 0 关注
  • 69 浏览
我要回答

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信