用于 Effort 单元测试的 Shim DbContext ctor
Shim DbContext ctor for Effort unit testing
我想拦截 var context = new MyDbContext()
到 return 不同的构造函数调用。
EFfort 的伟大之处在于它让您可以为单元测试设置一个简单的内存数据库。
var connection = Effort.DbConnectionFactory.CreateTransient();
var testContext = new MyDbContext(connection);
但是您必须将 context
注入您的存储库。
public FooRepository(MyDbContext context) { _context = context; }
是否可以只拦截 var context = new MyDbContext()
,以便 return 成为 testContext
?
using (var context = new MyDbContext()) {
// this way, my code isn't polluted with a ctor just for testing
}
您有两个可能的选择。使用工厂或通过面向方面的编程(如 PostSharp)
使用 PostSharp (AOP)
PostSharp is a great tool and can achieve the most clean interception
possible (meaning no changes in your classes and object generation at
all even if you do not your factories for object creation and/or
interfaces) but it is not a free library. Rather than creating proxies
at runtime, it injects code at compile time and therefore changes your
initial program in a seamless way to add method interception.
.....
The cool thing in this is that you do not change anything else in your
code, so your object can be still generated using the new keyword.
使用 DI 和工厂模式
我个人更喜欢工厂模式方法,但您似乎不愿意将任何依赖项注入您的 类。
public interface IDbContextFactory<T> where T : DbContext {
T Create();
}
public class TestDbContextFactory : IDbContextFactory<MyDbContext> {
public MyDbContext Create() {
var connection = Effort.DbConnectionFactory.CreateTransient();
var testContext = new MyDbContext(connection);
return testContext;
}
}
public class FooRepository {
MyDbContext _context;
public FooRepository(IDbContextFactory<MyDbContext> factory) {
_context = factory.Create();
}
}
(编辑:我刚刚意识到这实际上并没有返回另一个 ctor 调用。正在处理它。)
想通了。如果你知道怎么做就足够简单了:
[TestMethod]
public void Should_have_a_name_like_this()
{
// Arrange
var connection = Effort.DbConnectionFactory.CreateTransient();
ShimSolrDbContext.Constructor = context => new SolrDbContext(connection);
// Act
// Assert
}
和往常一样,EFfort 在 DbContext 中需要这个构造函数 class:
public class SomeDbContext
{
public SomeDbContext() : base("name=Prod")
{
}
// EFfort unit testing ctor
public SomeDbContext(DbConnection connection) : base(connection, contextOwnsConnection: true) {
Database.SetInitializer<SolrDbContext>(null);
}
}
但这意味着 repo 没有意识到特殊的瞬态连接:
public class SomeRepository
{
public void SomeMethodName()
{
using (var context = new SomeDbContext())
{
// self-contained in repository, no special params
// and still calls the special test constructor
}
}
}
我想拦截 var context = new MyDbContext()
到 return 不同的构造函数调用。
EFfort 的伟大之处在于它让您可以为单元测试设置一个简单的内存数据库。
var connection = Effort.DbConnectionFactory.CreateTransient();
var testContext = new MyDbContext(connection);
但是您必须将 context
注入您的存储库。
public FooRepository(MyDbContext context) { _context = context; }
是否可以只拦截 var context = new MyDbContext()
,以便 return 成为 testContext
?
using (var context = new MyDbContext()) {
// this way, my code isn't polluted with a ctor just for testing
}
您有两个可能的选择。使用工厂或通过面向方面的编程(如 PostSharp)
使用 PostSharp (AOP)
PostSharp is a great tool and can achieve the most clean interception possible (meaning no changes in your classes and object generation at all even if you do not your factories for object creation and/or interfaces) but it is not a free library. Rather than creating proxies at runtime, it injects code at compile time and therefore changes your initial program in a seamless way to add method interception.
.....
The cool thing in this is that you do not change anything else in your code, so your object can be still generated using the new keyword.
使用 DI 和工厂模式
我个人更喜欢工厂模式方法,但您似乎不愿意将任何依赖项注入您的 类。
public interface IDbContextFactory<T> where T : DbContext {
T Create();
}
public class TestDbContextFactory : IDbContextFactory<MyDbContext> {
public MyDbContext Create() {
var connection = Effort.DbConnectionFactory.CreateTransient();
var testContext = new MyDbContext(connection);
return testContext;
}
}
public class FooRepository {
MyDbContext _context;
public FooRepository(IDbContextFactory<MyDbContext> factory) {
_context = factory.Create();
}
}
(编辑:我刚刚意识到这实际上并没有返回另一个 ctor 调用。正在处理它。)
想通了。如果你知道怎么做就足够简单了:
[TestMethod]
public void Should_have_a_name_like_this()
{
// Arrange
var connection = Effort.DbConnectionFactory.CreateTransient();
ShimSolrDbContext.Constructor = context => new SolrDbContext(connection);
// Act
// Assert
}
和往常一样,EFfort 在 DbContext 中需要这个构造函数 class:
public class SomeDbContext
{
public SomeDbContext() : base("name=Prod")
{
}
// EFfort unit testing ctor
public SomeDbContext(DbConnection connection) : base(connection, contextOwnsConnection: true) {
Database.SetInitializer<SolrDbContext>(null);
}
}
但这意味着 repo 没有意识到特殊的瞬态连接:
public class SomeRepository
{
public void SomeMethodName()
{
using (var context = new SomeDbContext())
{
// self-contained in repository, no special params
// and still calls the special test constructor
}
}
}