Rog*_*ala 5 c# linq unit-testing entity-framework
我有一个Web API 2.0项目,我正在进行单元测试.我的控制器有一个工作单元.工作单元包含许多用于各种DbSet的存储库.我在Web API中有一个Unity容器,我在测试项目中使用Moq.在各种存储库中,我使用FindEntity 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
}
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是我Find在存储库中使用该方法进行一些LINQ查询.基于这篇文章,MSDN:使用您自己的测试双倍测试(EF6以上),我必须创建一个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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我想我必须自定义我的代码,这TEntity是一种具有Id属性的基类类型.这是我的理论,但我不确定这是处理这个问题的最好方法.
所以我有两个问题.上面列出的方法是否有效?如果没有,那么用方法覆盖Find方法会有什么更好DbSet的SingleOrDefault方法呢?此外,这种方法只有在它们只是一个主键时才能真正起作用.如果我的模型有不同类型的复合键怎么办?我认为我必须单独处理这些.好的,那是三个问题?
为了扩展我之前的评论,我将从我提出的解决方案开始,然后解释原因.
您的问题是:您的存储库依赖于DbSet<T>.您无法有效地测试存储库,因为它们依赖于DbSet<T>.Find(int[])您,因此您决定替换自己的DbSet<T>被调用变体TestDbSet<T>.这是不必要的; DbSet<T>实施IDbSet<T>.使用Moq,我们可以非常干净地创建此接口的存根实现,该实现返回硬编码值.
class MyRepository
{
public MyRepository(IDbSet<MyType> dbSet)
{
this.dbSet = dbSet;
}
MyType FindEntity(int id)
{
return this.dbSet.Find(id);
}
}
Run Code Online (Sandbox Code Playgroud)
通过将依赖关系切换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);
}
Run Code Online (Sandbox Code Playgroud)
那里 - 一个干净的测试,不暴露任何实现细节或处理具体类的讨厌模拟,并让你替换自己的版本IDbSet<MyType>.
另外,如果您发现自己正在测试DbContext- 请不要.如果你必须这样做,那么你DbContext的筹码太多了,如果你试图离开Entity Framework就会受到伤害.创建一个接口,公开您需要的功能DbContext并使用它.
注意:我上面使用了Moq.你可以使用任何模拟框架,我只是喜欢Moq.
如果你的模型有一个复合键(或者具有不同类型键的能力),那么事情会变得有点棘手.解决这个问题的方法是引入自己的界面.这个接口应该由您的存储库使用,并且实现应该是一个适配器,用于将密钥从您的复合类型转换为EF可以处理的内容.你可能会用这样的东西:
interface IGenericDbSet<TKeyType, TObjectType>
{
TObjectType Find(TKeyType keyType);
}
Run Code Online (Sandbox Code Playgroud)
然后,这将在实现中翻译成类似于:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1925 次 |
| 最近记录: |