使用 Ninject 模拟标准内核接口



我正在努力使用Moq和Ninject.MockingKernal将单元测试添加到一些遗留的ASP代码中。

public class BaseController : Controller
{
private IAuthenticationManager authenticationManager;
public ILog log;
public BaseController()
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
log = kernel.Get<ILog>();
}
}

日志接口:

public interface ILog
{
AuditTrail Create(AuditAction action, string description = null);
AuditTrail Create(AuditAction action, long reservationId, string description = null);
AuditTrail Create(IUser user, AuditAction action);
AuditTrail Create(IUser user, AuditAction action, string description = null);
AuditTrail Create(IUser user, AuditAction action, long reservationId, string description = null);
}

我正在尝试模拟从内核设置的日志实例。此日志由其他控制器继承,不会注入。我希望能够在请求时返回模拟对象,就像在其他情况下一样(例如从工厂返回模拟数据库上下文)。

我已经看过如何使用Ninject的MockingKernel(moq)和GitHub示例:https://github.com/ninject/Ninject.MockingKernel/wiki/Examples 以及许多其他示例进行模拟设置。

根据我收集的信息,我需要按照以下思路做一些事情:

mockingKernal = new MoqMockingKernel();
mockingKernal.Bind<ILog>().To<Logging.Log>();
var foo = mockingKernal.GetMock<ILog>();
foo.Setup(x => x.Create(It.IsAny<AuditAction>(), It.IsAny<long>(), It.IsAny<string>()));

但是,如果我运行它,我会收到一个错误System.ArgumentException: Object instance was not created by Moq.从我在网上找到的内容来看,这是由构造函数中具有参数的类引起的,但在这种情况下,Log 类没有。

我是否以正确的方式接近这一点?如果我是,我做错了什么?任何帮助将不胜感激

上述方法/设计将导致各种头痛的维护/测试,因为控制器与内核(IoC 容器)紧密耦合,这基本上不允许人们能够轻松模拟/替换它进行测试。

另请注意,所讨论的示例都具有将依赖项显式注入其所测试主题的能力。

以上基本上是使用内核作为服务定位器。

尝试在该代码上涂口红可能会改变其外观,但对气味没有任何影响。

理想情况下,设计应遵循显式依赖原则。

方法

和类应显式要求(通常通过方法参数或构造函数参数)它们需要的任何协作对象才能正常运行。

public class BaseController : Controller {
private IAuthenticationManager authenticationManager;
public ILog log;
public BaseController(ILog log, IAuthenticationManager authenticationManager) {
this.log = log;
this.authenticationManager = authenticationManager;
}
}

这将允许模拟/伪造/存根依赖项并将其注入其依赖项。

//Arrange
var logger = new Mock<ILog>();
logger
.Setup(_ => _.Create(It.IsAny<AuditAction>(), It.IsAny<long>(), It.IsAny<string>()))
.Return(new AuditTrail);
var controller = new BaseController(logger.Object, null);
//Act
//...

不应该直接在类中调用容器,而应该在组合根目录下配置它。

我建议查看当前的设计并相应地进行重构。

最新更新