[英]Using on UnitTest Mock-DbContext
在我的C#项目中,我使用.ToList();
查询.ToList();
在末尾:
public List<Product> ListAllProducts()
{
List<Product> products = db.Products
.Where(...)
.Select(p => new Product()
{
ProductId = p.ProductId,
...
})
.ToList();
return products;
}
有时,我会收到EntityException: "The underlying provider failed on Open."
错误。 我根据在SO上找到的答案做了以下变通方法
public List<Product> ListAllProducts()
{
try
{
// When accessing a lot of Database Rows the following error might occur:
// EntityException: "The underlying provider failed on Open." With an inner TimeoutExpiredException.
// So, we use a new db so it's less likely to occur
using(MyDbContext usingDb = new MyDbContext())
{
// and also set the CommandTimeout to 2 min, instead of the default 30 sec, just in case
((IObjectContextAdapter)usingDb).ObjectContext.CommandTimeout = 120;
List<Product> products = usingDb.Products
.Where(...)
.Select(p => new Product()
{
ProductId = p.ProductId,
...
})
.ToList();
return products;
}
}
// and also have a try-catch in case the error stil occurres,
// since we don't want the entire website to crash
catch (EntityException ex)
{
Console.WriteLine("An error has occured: " + ex.StackTrace);
return new List<Product>();
}
}
到目前为止,它似乎可以正常工作,并且每次都接收我的数据(以防万一我还添加了try-catch)。
现在我正在进行单元测试,并且遇到了问题。 我的UnitTest中有一个MockDbContext
,但是由于我使用的是using(...)
,它使用了非Mock版本,所以我的UnitTests失败了。 我如何使用MockDbContext而不是默认值进行测试?
编辑:
我刚刚碰到有关UnitTesting Mock DbContext的帖子 。 因此在我看来,我确实确实需要将其完全传递给我的方法,或者只是不使用using(MyDbContext usingDb = new MyDbContext())
,而是仅临时增加CommandTimeout并使用try-catch。 真的没有其他办法吗? 还是在没有一起using(MyDbContext usingDb = new MyDbContext())
情况下,针对我遇到的错误是否有更好的修复/解决方法?
编辑2:
为了使我的情况更加清楚:
我有MyDbContext类和IMyDbContext接口。
每个控制器中都包含以下内容:
public class ProductController : Controller
{
private IMyDbContext _db;
public ProductController(IMyDbContext db)
{
this._db = db;
}
... // Methods that use this "_db"
public List<Product> ListAllProducts()
{
try
{
using(MyDbContext usingDb = new MyDbContext())
{
... // Query that uses "usingDb"
}
}
catch(...)
{
...
}
}
}
在我的普通代码中,我创建如下实例:
ProductController controller = new ProductController(new MyDbContext());
在我的UnitTest代码中,我像这样创建实例:
ProductController controller = new ProductController(this.GetTestDb());
// With GetTestDb something like this:
private IMyDbContext GetTestDb()
{
var memoryProducts = new Product { ... };
...
var mockDB = new Mock<FakeMyDbContext>();
mockDb.Setup(m => m.Products).Returns(memoryProducts);
...
return mockDB.Object;
}
除了在ListAll方法中using(MyDbContext usingDb = new MyDbContext())
部分之外,其他所有东西都可以正常工作,无论是在我的普通项目中还是作为UnitTest。
虽然希望分解代码以促进测试,但是仅在测试内执行的代码分支无济于事。 确实,这是将测试环境配置代码放入您的实时代码中。
您需要的是创建DbContext
更多抽象DbContext
。 您应该传递一个构造DbContext
的委托:
public List<Product> ListAllProducts(Func<DbContext> dbFunc)
{
using(MyDbContext usingDb = dbFunc())
{
// ...
}
}
现在您不再需要传递繁重的DbContext
对象,您可以将方法参数重新分解到类构造函数中。
public class ProductDAL
{
private Func<DbContext> _dbFunc;
public ProductDAL(Func<DbContext> dbFunc)
{
_dbFunc = dbFunc;
}
public List<Product> ListAllProducts()
{
using(MyDbContext usingDb = _dbFunc())
{
// ...
}
}
}
最重要的是,您现在可以控制使用哪个DbContext
,包括从配置文件中拉出的连接字符串,一直到IOC容器中,而DbContext
担心代码中的其他任何地方。
以我的经验,如果不将代码结构化为对单元测试友好的话,就很难进行适当的单元测试,但是您可以尝试使该类通用,以便可以使用任一上下文将其实例化。
public class SomeClass<T> where T: MyDbContext, new()
{
public List<Product> ListAllProducts()
{
using(MyDbContext usingDb = new T())
{
((IObjectContextAdapter)usingDb).ObjectContext.CommandTimeout = 120;
List<Product> products = usingDb.Products.ToList();
return products;
}
}
}
// real-code:
var foo = new SomeClass<MyDbContext>();
// test
var bar = new SomeClass<MockContext>();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.