gpt4 book ai didi

c# - 如何在单元测试中实现 EF 的 FIND 方法?

转载 作者:太空狗 更新时间:2023-10-29 22:36:48 26 4
gpt4 key购买 nike

我有一个正在单元测试的 Web API 2.0 项目。我的 Controller 有一个工作单元。工作单元包含用于各种 DbSet 的大量存储库。我在 Web API 中有一个 Unity 容器,我在测试项目中使用 Moq。在各种存储库中,我使用 Find Entity Framework 的方法,用于根据实体的键定位实体。此外,我正在使用 Entity Framework 6.0。

这是工作单元的一个非常普遍的例子:

public class UnitOfWork
{
private IUnityContainer _container;
public IUnityContainer Container
{
get
{
return _container ?? UnityConfig.GetConfiguredContainer();
}
}

private ApplicationDbContext _context;
public ApplicationDbContext Context
{
get { _context ?? Container.Resolve<ApplicationDbContext>(); }
}

private GenericRepository<ExampleModel> _exampleModelRepository;
public GenericRepository<ExampleModel> ExampleModelRepository
{
get { _exampleModelRepository ??
Container.Resolve<GenericRepository<ExampleModel>>(); }
}

//Numerous other repositories and some additional methods for saving
}

我遇到的问题是我使用 Find存储库中我的一些 LINQ 查询的方法。基于本文,MSDN: Testing with your own test doubles (EF6 onwards) ,我必须创建一个 TestDbSet<ExampleModel>测试 Find方法。我正在考虑将代码自定义为如下内容:

namespace TestingDemo 
{
class TestDbSet : TestDbSet<TEntity>
{
public override TEntity Find(params object[] keyValues)
{
var id = (string)keyValues.Single();
return this.SingleOrDefault(b => b.Id == id);
}
}
}

我想我必须自定义我的代码,这样 TEntity是一些具有 Id 属性的基类的类型。这是我的理论,但我不确定这是处理此问题的最佳方法。

所以我有两个问题。上面列出的方法是否有效?如果不是,什么是覆盖 Find 的更好方法? DbSet 中的方法与 SingleOrDefault方法?此外,这种方法只有在它们只有一个主键时才真正有效。如果我的模型有不同类型的复合键怎么办?我想我将不得不单独处理这些。好的,那是三个问题?

最佳答案

为了进一步阐述我之前的评论,我将从我提出的解决方案开始,然后解释原因。

您的问题是:您的存储库依赖于 DbSet<T> .您无法有效地测试您的存储库,因为它们依赖于 DbSet<T>.Find(int[]) , 所以你决定替换你自己的 DbSet<T> 变体称为 TestDbSet<T> .这是不必要的; DbSet<T>工具 IDbSet<T> .使用 Moq,我们可以非常干净地创建此接口(interface)的 stub 实现,它返回一个硬编码值。

class MyRepository
{
public MyRepository(IDbSet<MyType> dbSet)
{
this.dbSet = dbSet;
}

MyType FindEntity(int id)
{
return this.dbSet.Find(id);
}
}

通过从 DbSet<T> 切换依赖关系至 IDbSet<T> ,测试现在看起来像这样:

public void MyRepository_FindEntity_ReturnsExpectedEntity()
{
var id = 5;
var expectedEntity = new MyType();
var dbSet = Mock.Of<IDbSet<MyType>>(set => set.Find(It.is<int>(id)) === expectedEntity));
var repository = new MyRepository(dbSet);

var result = repository.FindEntity(id);

Assert.AreSame(expectedEntity, result);
}

那里 - 一个干净的测试,不公开任何实现细节或处理具体类的讨厌模拟,并允许您替换自己的版本 IDbSet<MyType> .

附带说明一下,如果您发现自己正在测试 DbContext - 不。如果你必须这样做,你的 DbContext离堆栈太远,如果您尝试离开 Entity Framework 就会受到伤害。创建一个接口(interface),公开您需要的功能 DbContext并改用它。

注意:我在上面使用了 Moq。您可以使用任何模拟框架,我更喜欢 Moq。

如果您的模型有一个复合键(或具有不同类型键的能力),那么事情就会变得有点棘手。解决这个问题的方法是引入你自己的界面。该接口(interface)应该由您的存储库使用,并且实现应该是一个适配器,用于将 key 从您的复合类型转换为 EF 可以处理的内容。你可能会选择这样的东西:

interface IGenericDbSet<TKeyType, TObjectType>
{
TObjectType Find(TKeyType keyType);
}

然后这将在实现中转化为如下内容:

class GenericDbSet<TKeyType,TObjectType> 
{
GenericDbSet(IDbSet<TObjectType> dbset)
{
this.dbset = dbset;
}

TObjectType Find(TKeyType key)
{
// TODO: Convert key into something a regular dbset can understand
return this.dbset(key);
}
}

关于c# - 如何在单元测试中实现 EF 的 FIND 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31053951/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com