Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 要测试的实体框架4.1的虚拟DbContext_.net_Asp.net Mvc_Unit Testing_Entity Framework_Tdd - Fatal编程技术网

.net 要测试的实体框架4.1的虚拟DbContext

.net 要测试的实体框架4.1的虚拟DbContext,.net,asp.net-mvc,unit-testing,entity-framework,tdd,.net,Asp.net Mvc,Unit Testing,Entity Framework,Tdd,我使用本教程模拟我的DbContext并测试: 但我必须将FakeMainModuleContext实现更改为在控制器中使用: public class FakeQuestiona2011Context : IQuestiona2011Context { private IDbSet<Credencial> _credencial; private IDbSet<Perfil> _perfil; private IDbSet<Apurador

我使用本教程模拟我的DbContext并测试:

但我必须将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)
    }
}
公共类伪造问题2011上下文:iquestiona2011上下文
{
私人IDbSet _credencial;
专用IDbSet _perfil;
私有IDbSet_apurador;
私营企业;
专用IDbSet setor;
私人秘书处;
私有IDbSet_pesquisa;
私有IDbSet_pergunta;
私有IDbSet _resposta;
public IDbSet Credencial{get{return{u Credencial???(\u Credencial=new FakeDbSet());}set{}
公共IDbSet Perfil{get{return{u Perfil???(\u Perfil=new FakeDbSet());}set{}
公共IDbSet Apurador{get{return}u Apurador???(_Apurador=new FakeDbSet());}set{}
公共IDbSet Entrevistado{get{return}Entrevistado???(\u Entrevistado=new FakeDbSet());}set{}
公共IDbSet集合或{get{return}Setor???(\U Setor=new FakeDbSet());}set{}
公共IDbSet SEBRIA{get{return{U SEBRIA???(\U SERRIA=new FakeDbSet());}set{}
公共IDbSet PESQISA{get{return{U PESQISA???(\U PESQISA=new FakeDbSet());}set{}
公共IDbSet Pergunta{get{return{U Pergunta???(\U Pergunta=new FakeDbSet());}set{}
公共IDbSet Resposta{get{return}u Resposta???(_Resposta=new FakeDbSet());}set{}
公共void SaveChanges()
{
//不执行任何操作(可能将变量设置为已保存以供测试)
}
}
我的测试是这样的:

[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);
}
[TestMethod]
公共无效索引()
{
IQuestiona2011Context fakeContext=新的FakeQuestiona2011Context();
var mockAuthenticationService=new Mock();
var apuradores=新列表
{
new Apurador(){Matricula=“1234”,Nome=“Acaz Souza Pereira”,Email=”acaz@telecom.inf.br,Ramal=“1234”},
新Apurador(){Matricula=“4321”,Nome=“Samla Souza Pereira”,电子邮件=”samla@telecom.inf.br,Ramal=“4321”},
新的Apurador(){Matricula=“4213”,Nome=“Valderli Souza Pereira”,电子邮件=”valderli@telecom.inf.br,Ramal=“4213”}
};
ForEach(apurador=>fakeContext.apurador.Add(apurador));
ApuradorController ApuradorController=新的ApuradorController(fakeContext,mockAuthenticationService.Object);
ActionResult ActionResult=apuradorController.Index();
Assert.IsNotNull(actionResult);
IsInstanceOfType(actionResult,typeof(ViewResult));
ViewResult ViewResult=(ViewResult)actionResult;
Assert.IsInstanceOfType(viewResult.ViewData.Model,typeof(IndexViewModel));
IndexViewModel IndexViewModel=(IndexViewModel)viewResult.ViewData.Model;
arenequal(3,indexViewModel.Apuradores.Count);
}

我做得对吗?

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

我的意见是:如果您想将集合公开给控制器,就不要费心伪造上下文。而是使用与真实数据库的集成测试进行测试。这是验证控制器中定义的LINQ查询是否达到预期效果的唯一方法

当然,如果您只想在集合上调用
ToList
FirstOrDefault
,您的
FakeContext
将很好地为您服务,但一旦您做了更复杂的事情,您很快就会发现一个陷阱(只需输入字符串“无法转换为存储表达式”进入谷歌-所有这些问题只会在您运行Linq to实体时出现,但它们会通过您使用Linq to对象的测试)

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


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

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

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

. 

Database.SetInitializer(新的DropCreateDatabaseAlways());
然后,在运行每个测试之前强制EF初始化

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

    context = new MyDbContext();
    context.Database.Initialize(force: true);
}
公共作废设置(){
SetInitializer(新的DropCreateDatabaseAlways());
context=新的MyDbContext();
context.Database.Initialize(强制:true);
}
如果您使用的是xunit,请在构造函数中调用Setup方法。如果您使用的是MSTest,请将TestInitializeAttribute放在该方法上。如果是

“不幸的是,你做得不对,因为那篇文章是错误的。它假装FakeContext会使你的代码单元可测试,但它不会”

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

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

示例:

在我的MVC应用程序中,我们使用代表
public void Setup() {
    Database.SetInitializer<MyDbContext>(new DropCreateDatabaseAlways<MyDbContext>());

    context = new MyDbContext();
    context.Database.Initialize(force: true);
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;

namespace MockFactory
{
    public class TestDbSet<TEntity> : DbSet<TEntity>, IQueryable, IEnumerable<TEntity>, IDbAsyncEnumerable<TEntity>
        where TEntity : class
    {
        public readonly ObservableCollection<TEntity> _data;
        private readonly IQueryable _query;
        private readonly Dictionary<Type, object> entities;

        public TestDbSet()
        {
            _data = new ObservableCollection<TEntity>();
            _query = _data.AsQueryable();

            entities = new Dictionary<Type, object>();
        }

        public override ObservableCollection<TEntity> Local
        {
            get { return _data; }
        }

        IDbAsyncEnumerator<TEntity> IDbAsyncEnumerable<TEntity>.GetAsyncEnumerator()
        {
            return new TestDbAsyncEnumerator<TEntity>(_data.GetEnumerator());
        }

        IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
        {
            return _data.GetEnumerator();
        }

        Type IQueryable.ElementType
        {
            get { return _query.ElementType; }
        }

        Expression IQueryable.Expression
        {
            get { return _query.Expression; }
        }

        IQueryProvider IQueryable.Provider
        {
            get { return new TestDbAsyncQueryProvider<TEntity>(_query.Provider); }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return _data.GetEnumerator();
        }

        public void AddNavigationProperty<T>(DbSet<T> dbSet) where T : class
        {
            entities.Add(typeof (T), dbSet);
        }

        public void RefreshNavigationProperty(TEntity item)
        {
            foreach (var entity in entities)
            {
                var property = item.GetType().GetProperty(entity.Key.Name);

                var type =
                    (int)item.GetType().GetProperty(entity.Key.Name.Replace(typeof(TEntity).Name, "")).GetValue(item);

                var dbSets = (IEnumerable<object>)entity.Value.GetType().GetField("_data").GetValue(entity.Value);

                var dbSet = dbSets.Single(x => (int)x.GetType().GetProperty("Id").GetValue(x) == type);
                property.SetValue(item, dbSet);
            }
        }

        public override TEntity Add(TEntity item)
        {
            RefreshNavigationProperty(item);
            _data.Add(item);
            return item;
        }

        public override TEntity Remove(TEntity item)
        {
            _data.Remove(item);
            return item;
        }

        public override TEntity Attach(TEntity item)
        {
            _data.Add(item);
            return item;
        }

        public override TEntity Create()
        {
            return Activator.CreateInstance<TEntity>();
        }

        public override TDerivedEntity Create<TDerivedEntity>()
        {
            return Activator.CreateInstance<TDerivedEntity>();
        }
    }
}
 public TestContext()
        {
            TypeUsers = new TestDbSet<TypeUser>();
            StatusUsers = new TestDbSet<StatusUser>();

            TypeUsers.Add(new TypeUser {Description = "FI", Id = 1});
            TypeUsers.Add(new TypeUser {Description = "HR", Id = 2});

            StatusUsers.Add(new StatusUser { Description = "Created", Id = 1 });
            StatusUsers.Add(new StatusUser { Description = "Deleted", Id = 2 });
            StatusUsers.Add(new StatusUser { Description = "PendingHR", Id = 3 });


            Users = new TestDbSet<User>();

            ((TestDbSet<User>) Users).AddNavigationProperty(StatusUsers);
           ((TestDbSet<User>)Users).AddNavigationProperty(TypeUsers);

        }

        public override DbSet<TypeUser> TypeUsers { get; set; }
        public override DbSet<StatusUser> StatusUsers { get; set; }
        public override DbSet<User> Users { get; set; }
        public int SaveChangesCount { get; private set; }

        public override int SaveChanges(string modifierId)
        {
            SaveChangesCount++;
            return 1;
        }
    }
ContextFactory.Entity.Users.Each(((TestDbSet<User>) ContextFactory.Entity.Users).RefreshNavigationProperty);