我有一个实体框架DbContext
,我们称之为SomeEntities
。我的团队中另一位不再在这的成员为SomeEntities
编写了一个扩展方法,现在使用遍布我们的应用程序。
public static bool SaveWithAudit(this SomeEntities context, string activity, int userId)
{
context.SaveChanges();
logWhatHappened(userId); //there's more here, doing this for brevity
}
我正在为我们的服务层进行单元测试,该服务层与数据上下文交互,并且学会了我can't test static members with Moq的艰难方式。我使用的是Microsoft Fakes,但已经学会了单元测试using Fakes don't show up in dotCover results的难点。在我的研究中,我已经看到一些人们已经包装或创建自己的存根来处理这些情况的情况,但我还没有找到一个对我的场景有意义的具体示例。所以我的问题是:有没有办法可以存根和/或设置一个包装器,允许我模拟这个扩展方法进行单元测试?
答案 0 :(得分:1)
SaveWithAudit
方法是一种静态方法,因此您无法模拟它(您可以尝试使用commercial mocking framework)。
但是,您可以通过将SomeEntities包装到接口
中,将逻辑从扩展方法移动到您的上下文public interface ISomeEntitiesContext
{
int SaveChanges();
bool SaveWithAudit(this SomeEntities context, string activity, int userId);
// add other methods if required
}
上下文应该实现ISomeEntitiesContext
以及DbContext
public partial class SomeEntitiesContext : DbContext, ISomeEntitiesContext
{
public bool SaveWithAudit(this SomeEntities context, string activity, int userId)
{
this.SaveChanges();
logWhatHappened(userId);
}
}
通过这种方式,您可以在服务层中使用ISomeEntitiesContext.SaveWithAudit
方法,而无需模拟扩展方法。
此解决方案违反单一责任原则,因为现在SomeEntitiesContext
知道如何保存实体以及如何记录审核消息。如果您有IoC容器,它可能具有拦截器功能。在我们的项目中,所有审核都是使用Castle Windsor interceptors完成的,我们的代码没有log.LogMessage("...")
的讨厌条目。