如何模拟对Web API控制器单元测试的依赖关系
本文关键字:控制器 API 单元测试 关系 依赖 Web 何模拟 模拟 | 更新日期: 2023-09-27 17:52:17
在我当前的MVC应用程序中,我构建了一系列命令对象来处理业务操作。这些业务操作将围绕服务端点进行。这些端点也将被MVC前端&windows应用程序。每个业务操作都将调用一个DAO操作,而DAO操作又调用所需的数据访问存储库来成功执行业务操作。我在下面列出了一个动作示例。
业务行动
public class CreateProjectAction
{
IInsertProjectDAOAction InsertProjectDAOAction { get; set; }
public void Execute()
{
// Does some business validation & other logic before
// calling the DAO action
InsertProjectDAOAction.Execute();
}
}
DAO操作
public interface IInsertProjectDAOAction
{
void Execute();
}
public class InsertProjectDAOAction
{
IProjectRepository ProjectRepository { get; set; }
public void Execute()
{
ProjectRepository.Insert();
}
}
项目存储库
public interface IProjectRepository
{
void Insert(Project proj);
// other db methods would be listed here
}
public class ProjectRepository
{
public void Insert(Project proj)
{
// Insert into the data store
}
}
控制器
[HttpPost]
public IHttpActionResult Create(NewProjectModel newProjectModel)
{
var cmdArgs = Mapper.Map<CreateProjectCommand.CreateProjectCommandArgs>(newProjectModel);
var action = new CreateProjectCommand(UserId, cmdArgs);
action.Execute();
if(action.IsSuccessful)
return Ok(project)
else
return InternalServerError(action.Exception);
}
单元测试
public void InsertWith_ExistingProjectName_Returns_ServerError()
{
var arg = new CreateProjectCommandArgs(){ .... };
var cmd = CreateProjectAction(args);
action.Execute();
Assert.That(action.IsSuccessful, Is.False);
Assert.That(action.Exception, Is.TypeOf<UniqueNameExcepton>());
}
我正在使用Ninject来帮助层之间的依赖关系注入。我围绕业务"CreateProjectAction"进行了一系列单元测试,以测试该对象的预期行为。业务操作围绕一系列Web API服务端点进行。我还想围绕我的MVC控制器编写测试,这样我就可以确保它们按计划工作。
到目前为止,我喜欢这个架构,但在为mvc控制器编写单元测试时,我很难弄清楚如何模拟业务操作中的DAO操作属性。我很乐意听取建议、其他观点等。。。
您的问题仍然有点不清楚。例如,InsertProjectDAOAction
可能实现了接口IInsertProjectDAOAction
,尽管您的示例代码没有表明它实现了。还不清楚控制器示例中的CreateProjectCommand
是什么,因为它不是上面的示例元素之一
也就是说,可以采取的一种方法是将命令的创建推迟到工厂,并将工厂注入控制器(通过代码中的Ninject和单元测试中的Mock(。这允许您设置一个模拟链。你模拟工厂,让它返回你感兴趣的动作的模拟,然后你可以设置它来做任何你想做的事情。在一个非常基本的层面上,这可能看起来像这样:
public interface ICommandFactory {
IInsertProjectDAOAction CreateInsertProjectAction(int userId);
}
public class CommandFactory : ICommandFactory{
public IInsertProjectDAOAction CreateInsertProjectAction(int userId) {
return new InsertProjectDAOAction(/* userId???? */);
}
}
控制器会这样做来使用工厂:
public IHttpActionResult Create(/* ... */) {
var action = _commandFactory.CreateInsertProjectAction(1234);
action.Execute();
// ...
}
测试看起来像:
[Test]
public void MyTest() {
var factoryMock = new Mock<ICommandFactory>();
var commandMock = new Mock<IInsertProjectDAOAction>();
factoryMock.Setup(x => x.CreateInsertProjectAction(It.IsAny<int>())).Returns(commandMock.Object);
commandMock.Setup(x => x.Execute()).Throws(new InvalidOperationException("Random failure"));
var controller = new MyController(factoryMock.Object);
try {
controller.Create(/* ... */);
Assert.Fail();
}
catch (InvalidOperationException ex) {
Assert.AreEqual("Random failure", ex.Message);
}
}
这是可以采用的通用方法。然而,正如我所说,这可能不适合你的情况,因为你的问题不清楚。我也忽略了关于如何创建/测试控制器的其他问题,因为这似乎不是你的问题所在。。。