簡體   English   中英

EF 6偽造的數據庫上下文,找不到實體

[英]EF 6 fake db context, can't find the entity

我正在用單元測試介紹一些服務類,並且已經設法使用NSubstitute隔離/偽造了dbcontext(遵循指南)。 我已經完成一些測試並且可以正常工作,並且一切似乎都還不錯,但是現在我找不到添加到上下文中的實體。

測試代碼非常簡單:

[Fact]
public void CreateStore_GivenAccount_AccountIsAssignedTheStore()
{
    const int accountId = 10;
    var account = new Account {Id = accountId};
    var fakeContext = new FakeContextBuilder()
        .WithAccounts(account)
        .Build();
    var service = new Service(fakeContext);
    const int someProperty = 0;
    const string someOtherProperty = "blabla";

    service.CreateStore(accountId, someProperty, someOtherProperty);

    var storeWasAdded = account.Stores
        .Any(store =>
            store.SomeProperty == someProperty &&
            store.SomeOtherProperty == someOtherProperty);
    Assert.True(storeWasAdded);
}

FakeContextBuilder是我為設置上下文而FakeContextBuilder的幫助器類(其他實體的類似方法):

public class FakeContextBuilder
{
    private DbSet<Account> _accountTable;

    private static DbSet<TEntity> SetUpFakeTable<TEntity>(params TEntity[] entities) where TEntity : class
    {
        var fakeTable = Substitute.For<DbSet<TEntity>, IQueryable<TEntity>>() as IQueryable<TEntity>;
        var table = entities.AsQueryable();
        fakeTable.Provider.Returns(table.Provider);
        fakeTable.Expression.Returns(table.Expression);
        fakeTable.ElementType.Returns(table.ElementType);
        fakeTable.GetEnumerator().Returns(table.GetEnumerator());
        return (DbSet<TEntity>) fakeTable;
    }

    public Context Build()
    {
        var context = Substitute.For<Context>();
        context.Accounts.Returns(_accountTable);
        return context;
    }

    public FakeContextBuilder WithAccounts(params Account[] accounts)
    {
        _accountTable = SetUpFakeTable(accounts);
        return this;
    }
}

服務方式:

public void CreateStore(int accountID, int someProperty, string someOtherProperty)
{
    var account = _context.Accounts.Find(accountID);
    account.Stores.Add(new Store(someProperty, someOtherProperty));
}

Accounts.Find()行上,我得到的是null而不是預期的帳戶實例。 如果我添加一個斷點並查看上下文,我會看到Accounts上的“枚舉結果”不會產生任何結果,但是我可以看到在非公共成員中正確設置了提供者和枚舉器等。 偽造的上下文生成器在其他測試中也可以正常工作,因此我猜測這與Find()方法有關。

編輯:我現在已經確認Find()方法是罪魁禍首,因為在執行此操作時測試通過了:

var account = _context.Accounts.Single(act => act.Id == 10);

我仍然想將Find()用於緩存等。 可以以某種方式在測試代碼中進行配置嗎? 因為它實際上是一個簡單的操作,所以不希望為此而弄亂生產代碼。

我已經解決了問題。 它可能不是有史以來最整潔的解決方案,但似乎可以解決問題,而且我看不到(至少目前是這樣),以后再也不會對維護造成麻煩。

我通過創建一個DbSet<T>的子類將其拉開了,我想像得足夠多,名為DbSetWithFind<T>

public class DbSetWithFind<TEntity> : DbSet<TEntity> where TEntity : class
{
    private readonly IQueryable<TEntity> _dataSource;

    public DbSetWithFind(IQueryable<TEntity> dataSource)
    {
        _dataSource = dataSource;
    }

    public sealed override TEntity Find(params object[] keyValues) // sealed override prevents EF from "ruining" it.
    {
        var keyProperties = typeof (TEntity).GetProperties()
            .Where(property => property.IsDefined(typeof (KeyAttribute), true));
        return _dataSource.SingleOrDefault(entity =>
            keyProperties
                .Select(property => property.GetValue(entity))
                .Intersect(keyValues)
                .Any());
    }
}

然后,我剛剛修改了Substitute.For()調用以使用子類,其中包含我的Find()的自定義實現。

private static DbSet<TEntity> SetUpFakeTable<TEntity>(params TEntity[] entities) where TEntity : class
{
    var dataSource = entities.AsQueryable();
    var fakeDbSet = Substitute.For<DbSetWithFind<TEntity>, IQueryable<TEntity>>(dataSource); // changed type and added constructor params
    var fakeTable = (IQueryable<TEntity>) fakeDbSet;
    fakeTable.Provider.Returns(dataSource.Provider);
    fakeTable.Expression.Returns(dataSource.Expression);
    fakeTable.ElementType.Returns(dataSource.ElementType);
    fakeTable.GetEnumerator().Returns(dataSource.GetEnumerator());

    return (DbSet<TEntity>) fakeTable;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM