C# 如何模拟Breeze.EFContextProvider?更新

C# 如何模拟Breeze.EFContextProvider?更新,c#,dependency-injection,inversion-of-control,breeze,repository,C#,Dependency Injection,Inversion Of Control,Breeze,Repository,此后,我尝试继承EFContextProvider,以便可以将上下文作为虚拟属性提供以允许测试,并可以将上下文提供程序注入存储库。这种新模式确实适用于IOC,但单元测试仍然失败,出现以下错误: 试验方法 MyProj.Infrastructure.MyItemTests.The_MyRepository_MyItems_Should.Return_MyItem_实例的可检索列表_引发异常: System.NullReferenceException:对象引用未设置为对象的实例 /********

此后,我尝试继承EFContextProvider,以便可以将上下文作为虚拟属性提供以允许测试,并可以将上下文提供程序注入存储库。这种新模式确实适用于IOC,但单元测试仍然失败,出现以下错误:

试验方法 MyProj.Infrastructure.MyItemTests.The_MyRepository_MyItems_Should.Return_MyItem_实例的可检索列表_引发异常: System.NullReferenceException:对象引用未设置为对象的实例

/************更新#2*********/ 添加了存储库,因此我的示例代码更加清晰

 /*************************** UPDATED *****************/
/*******************支持国际奥委会的新模式******************************/
公共接口IMyDbContextProvider{
MyDbContext上下文{get;}
字符串元数据{get;}
SaveResult SaveChanges(JObject saveBundle);
}
公共类MyDbContext:BaseContext、IMyDbContext{
公共虚拟数据库集MyItem{get;set;}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder){
//使用单数表名
modelBuilder.Conventions.Remove();
//禁用代理创建和延迟加载;此服务上下文中不需要。
Configuration.ProxyCreationEnabled=false;
Configuration.LazyLoadingEnabled=false;
添加(新的MyItemMap());
}
}
公共类MyContextProvider:EFContextProvider、IMyContextProvider{
公共MyContextProvider(){
}
公共虚拟MyDbContext上下文{
获取{return base.Context;}
}
公共新字符串元数据{
得到{
返回base.Metadata();
}
}
公共虚拟SaveResult SaveChanges(JObject saveBundle){
返回base.SaveChanges(saveBundle);
}
受保护的覆盖bool BeforeSaveEntity(EntityInfo EntityInfo){
返回base.BeforeSaveEntity(entityInfo);
}
}
公共接口IMyRepository{
字符串元数据{get;}
IQueryable GetMyItems{get;}
SaveResult SaveChanges(JObject saveBundle);
}
公共类MyRepository:IMyRepository{
私有只读IMyContextProvider\u myContextProvider;
公共MyRepository(IMyContextProvider myContextProvider){
_myContextProvider=myContextProvider;
}
公共MyDbContext上下文{
得到{
返回_myContextProvider.Context;
}
}
公共字符串元数据{
得到{
return\u myContextProvider.Metadata;
}
}
public SaveResult SaveChanges(JObject saveBundle){
return\u myContextProvider.SaveChanges(saveBundle);
}
公共IQueryable GetMyItems(){
得到{
返回Context.MyItems;
}
}
}
/**********************我的单元测试设置功能和测试功能****/
[测试初始化()]
公共无效设置(){
_queryablemyitemdata=GetQueryableMyItemData();//返回MyItems的列表。AsQueryable()
_mockMyContextProvider=新建Mock();
_mockMyItemSet=newmock();
_mockMyItemSet.As().Setup(m=>m.Provider).Returns(_queryMyItemData.Provider);
_mockMyItemSet.As().Setup(m=>m.Expression).Returns(_queryMyItemData.Expression);
_mockMyItemSet.As().Setup(m=>m.ElementType).Returns(_queryMyItemData.ElementType);
_mockMyItemSet.As().Setup(m=>m.GetEnumerator()).Returns(_queryMyItemData.GetEnumerator());
_mockMyContextProvider.Setup(m=>m.Context.Units)。返回(_mockUnitSet.Object);
}
[测试方法]
public void Return_A_iquitable_List_of_MyItem_实例(){
var expectedType=typeof(IEnumerable);
var myRepository=newmyrepository(_mockMyContextProvider.Object);
var resultingMyItems=myRepository.GetMyItems();
IsInstanceOfType(resultingMyItems,expectedType);
}

我没有仔细看,就注意到了一些事情

首先,您没有告诉我们是什么引发了
NullReferenceException
。“对象引用未设置为对象的实例”不是有用的消息,因此我们也无法帮助您

其次,您正试图在
MyDbContextProvider
中虚拟化基本
EFContextProvider
类中非虚拟的成员。你可以认为这些成员应该是虚拟的(我会和你并肩站在一起……请为此创建一个UserVoice)

但是基类成员不是虚拟的,因此您可能会遇到这样的风险:在此过程中,其他一些方法将强制转换到基类
EFContextProvider
,并尝试访问未修改的实现。我不知道在你的情况下会发生这种情况,但这显然是一种可能性

第三,如果您的测试设置必须模拟
IQueryable
类的成员(例如,
Provider
Expression
ElementType
),那么您的工作太辛苦了。这对于有效的测试来说是太多的摩擦了。您需要另一种方法使存储库可测试

第四,因为您没有显示存储库(不用麻烦了……已经有太多代码了),所以我无法说出
myRepository.myItems
与测试模拟的关系

关于
EFContextProvider
太难伪造的问题,你有一个很好的观点。它不是为可测试性而正确编写的。公平地说,EF团队没有提供太多帮助,因为
DbContext
ObjectContext
看起来也不容易虚拟化


但是您自己还有其他问题需要首先解决。

你好,沃德,非常感谢您的回复。添加了示例存储库,使我的示例代码更加完整。无论如何,我注意到测试设置方法没有TestInitialize属性。现在考试通过了。测试的原因是确保我得到了预期的类型
/********************** THE NEW PATTERN TO SUPPORT IOC ******************************/

public interface IMyDbContextProvider {

    MyDbContext Context { get; }
    string Metadata { get; }
    SaveResult SaveChanges( JObject saveBundle );

}

public class MyDbContext : BaseContext<MyDbContext>, IMyDbContext  {

    public virtual DbSet<MyItem> MyItem{ get; set; }

    protected override void OnModelCreating( DbModelBuilder modelBuilder ) {
        // Use singular table names
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>( );

        // Disable proxy creation and lazy loading; not wanted in this service context.
        Configuration.ProxyCreationEnabled = false;
        Configuration.LazyLoadingEnabled = false;

        modelBuilder.Configurations.Add(new MyItemMap());


    }
}


public class MyContextProvider : EFContextProvider <MyDBContext> , IMyContextProvider {

    public MyContextProvider( ) {

    }
    public virtual MyDbContext Context {
        get { return base.Context; }
    }

    public new string Metadata {
        get {
            return base.Metadata();
        }
    }

    public virtual SaveResult SaveChanges( JObject saveBundle ) {
        return base.SaveChanges( saveBundle );
    }

    protected override bool BeforeSaveEntity( EntityInfo entityInfo ) {
        return base.BeforeSaveEntity( entityInfo );
    }

}

     public interface IMyRepository {

    string Metadata { get; }
    IQueryable<MyItem> GetMyItems{ get; }
    SaveResult SaveChanges( JObject saveBundle );

}
 public class MyRepository : IMyRepository {

    private readonly IMyContextProvider _myContextProvider;

    public MyRepository (IMyContextProvider myContextProvider ) {
        _myContextProvider = myContextProvider;
    }

    public MyDbContext Context {
        get {
            return _myContextProvider.Context;
        }
    }

    public string Metadata {
        get {
            return _myContextProvider.Metadata;
        }
    }

    public SaveResult SaveChanges(JObject saveBundle) {
        return _myContextProvider.SaveChanges( saveBundle );
    }

    public IQueryable<MyItem> GetMyItems() {
        get {
            return Context.MyItems;
        }
    }


}

/**********************My UNIT TEST SET UP FUNCTION AND TEST FUNCTION****/
       [TestInitialize()]
        public void SetUp( ) {
        _querableMyItemData = GetQueryableMyItemData( ); //Returns a list of                  MyItems.AsQueryable()


        _mockMyContextProvider = new Mock<IMyContextProvider>( );

        _mockMyItemSet = new Mock<DbSet<MyItem>>( );
        _mockMyItemSet.As<IQueryable<MyItem>>( ).Setup( m => m.Provider ).Returns( _querableMyItemData.Provider );
        _mockMyItemSet.As<IQueryable<MyItem>>( ).Setup( m => m.Expression ).Returns( _querableMyItemData.Expression );
        _mockMyItemSet.As<IQueryable<MyItem>>( ).Setup( m => m.ElementType ).Returns( _querableMyItemData.ElementType );
        _mockMyItemSet.As<IQueryable<MyItem>>( ).Setup( m => m.GetEnumerator( ) ).Returns( _querableMyItemData.GetEnumerator( ) );


        _mockMyContextProvider.Setup( m => m.Context.Units ).Returns( _mockUnitSet.Object );

    }

    [TestMethod]
    public void Return_A_IQuerable_List_of_MyItem_Instances( ) {
        var expectedType = typeof( IEnumerable<MyItem> );

        var myRepository = new MyRepository( _mockMyContextProvider.Object );

        var resultingMyItems = myRepository.GetMyItems();

        Assert.IsInstanceOfType( resultingMyItems, expectedType );
    }