如何通过 Wrapper 模拟上下文
How to Mock Context through Wrapper
我有一种情况是通过包装器使用上下文。包装器用于服务层。问题是在这种情况下如何模拟上下文?
上下文
public class DbContext : System.Data.Entity.DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
base.OnModelCreating(modelBuilder);
}
public DbSet<Report> Reports { get; set; }
}
包装器
public interface IDbContextWrapper<out TContext> where TContext : System.Data.Entity.DbContext, new()
{
T Call<T>(Func<TContext, T> func);
void Call(Action<TContext> action);
}
包装器实现
public class DbContextWrapper<TContext> : IDbContextWrapper<TContext> where TContext : System.Data.Entity.DbContext, new()
{
public virtual TContext BuildContext(bool enableChangeTracking = true, bool enableLazyLoading = false, bool enableProxyCreation = false)
{
var result = new TContext();
result.Configuration.AutoDetectChangesEnabled = enableChangeTracking;
result.Configuration.LazyLoadingEnabled = enableLazyLoading;
result.Configuration.ProxyCreationEnabled = enableProxyCreation;
return result;
}
public virtual T Call<T>(Func<TContext, T> func)
{
using (var context = BuildContext())
{
return func(context);
}
}
public virtual void Call(Action<TContext> action)
{
using (var context = BuildContext())
{
action(context);
}
}
}
以及我需要使用 Mocks 进行单元测试的服务及其方法
public class ReportService : IReportService
{
private readonly IDbContextWrapper<DbContext> _contextWrapper;
public ReportService(IDbContextWrapper<DbContext> contextWrapper)
{
_contextWrapper = contextWrapper;
}
public Report GetPreviousReport(int currentReportId, int lineId)
{
return _contextWrapper.Call(
context => context.Reports
.Where(item => item.Id < currentReportId && item.LineId == lineId)
.OrderBy(item => item.Id)
.First());
}
public Report GetNextReport(int currentReportId, int lineId)
{
return _contextWrapper.Call(
context => context.Reports
.Where(item => item.Id > currentReportId && item.LineId == lineId)
.OrderBy(item => item.Id)
.First());
}
}
在您的测试中,您可以创建 StubTContext 并将报告初始化为 return 测试数据。然后用 IDbContextWrapper 初始化您的被测系统 ReportService。
完成此准备后,使用 Moq 模拟 IDbContextWrapper,如下所示:
Mock<IDbContextWrapper<StubTContext>> mock = new Mock<IDbContextWrapper<StubTContext>>();
mock.Setup(m => m.Call(It.IsAny<Action<StubTContext>>())).Callback<Action<StubTContext>>(a => a.Invoke(new StubTContext()));
您还可以实现 StubTContext 以通过构造函数提供测试数据,然后仅在模型期间将测试数据传递给它。像这样:
public StubTContext : DbContext
{
public StubTContex(DbSet<Report> reports)
{
Reports = reports;
}
}
然后在模拟中
mock.Setup(m => m.Call(It.IsAny<Action<StubTContex>>())).Callback<Action<StubTContex>>(a => a.Invoke(new StubTContex(*YOUR REPORTS*)));
我有一种情况是通过包装器使用上下文。包装器用于服务层。问题是在这种情况下如何模拟上下文?
上下文
public class DbContext : System.Data.Entity.DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
base.OnModelCreating(modelBuilder);
}
public DbSet<Report> Reports { get; set; }
}
包装器
public interface IDbContextWrapper<out TContext> where TContext : System.Data.Entity.DbContext, new()
{
T Call<T>(Func<TContext, T> func);
void Call(Action<TContext> action);
}
包装器实现
public class DbContextWrapper<TContext> : IDbContextWrapper<TContext> where TContext : System.Data.Entity.DbContext, new()
{
public virtual TContext BuildContext(bool enableChangeTracking = true, bool enableLazyLoading = false, bool enableProxyCreation = false)
{
var result = new TContext();
result.Configuration.AutoDetectChangesEnabled = enableChangeTracking;
result.Configuration.LazyLoadingEnabled = enableLazyLoading;
result.Configuration.ProxyCreationEnabled = enableProxyCreation;
return result;
}
public virtual T Call<T>(Func<TContext, T> func)
{
using (var context = BuildContext())
{
return func(context);
}
}
public virtual void Call(Action<TContext> action)
{
using (var context = BuildContext())
{
action(context);
}
}
}
以及我需要使用 Mocks 进行单元测试的服务及其方法
public class ReportService : IReportService
{
private readonly IDbContextWrapper<DbContext> _contextWrapper;
public ReportService(IDbContextWrapper<DbContext> contextWrapper)
{
_contextWrapper = contextWrapper;
}
public Report GetPreviousReport(int currentReportId, int lineId)
{
return _contextWrapper.Call(
context => context.Reports
.Where(item => item.Id < currentReportId && item.LineId == lineId)
.OrderBy(item => item.Id)
.First());
}
public Report GetNextReport(int currentReportId, int lineId)
{
return _contextWrapper.Call(
context => context.Reports
.Where(item => item.Id > currentReportId && item.LineId == lineId)
.OrderBy(item => item.Id)
.First());
}
}
在您的测试中,您可以创建 StubTContext 并将报告初始化为 return 测试数据。然后用 IDbContextWrapper 初始化您的被测系统 ReportService。
完成此准备后,使用 Moq 模拟 IDbContextWrapper,如下所示:
Mock<IDbContextWrapper<StubTContext>> mock = new Mock<IDbContextWrapper<StubTContext>>();
mock.Setup(m => m.Call(It.IsAny<Action<StubTContext>>())).Callback<Action<StubTContext>>(a => a.Invoke(new StubTContext()));
您还可以实现 StubTContext 以通过构造函数提供测试数据,然后仅在模型期间将测试数据传递给它。像这样:
public StubTContext : DbContext
{
public StubTContex(DbSet<Report> reports)
{
Reports = reports;
}
}
然后在模拟中
mock.Setup(m => m.Call(It.IsAny<Action<StubTContex>>())).Callback<Action<StubTContex>>(a => a.Invoke(new StubTContex(*YOUR REPORTS*)));