使用 Moq 测试视图模型构造函数参数



我有一个用 ASP.NET Core 编写的基本 CRUD 应用程序,其中的示例控制器操作如下所示:

public IActionResult Sheet(Guid characterId)
{
var character = _repository.GetCharacter(characterId);
// omit validation for brevity
return View("Sheet", new ViewModel(character, true, true));
}

还有一个示例单元测试:

[Fact]
public void TestSheet()
{
// Arrange stuff and mock setup
// Act
var result = _controller.Sheet(characterId) as ViewResult;
// Assert
Assert.NotNull(result);
// assert ViewModel constructor called with x arguments
}

其他研究似乎建议对物体进行模拟,但这不适合这种情况。 我可以简单地让ViewModel构造函数设置几个 getter 属性,但这似乎是在深入研究测试ViewModel领域,而不是控制器。控制器不应该关心 ViewModel 如何处理传递给它的值,在我看来,测试也不应该 - 测试是测试控制器的行为是否正确,并且实际上应该只测试它是否传递了正确的值。几个 getter 属性肯定会实现这一目标,但它似乎在概念上是错误的。

该操作与视图模型的创建紧密耦合,这可以反转

public interface IViewModelFactory {
T CreateNew<T>(params object[] args) where T : ViewModelBase;
}
public class ViewModelFactory : IViewModelFactory {
public T CreateNew<T>(params object[] args) where T : ViewModelBase {
return (T)Activator.CreateInstance(typeof(T), args);
}
}
public class ViewModelBase { }

因此,控制器不负责创建/初始化视图模型。这将是注入控制器并用于创建视图模型的显式依赖项。

public IActionResult Sheet(Guid characterId) {
var character = _repository.GetCharacter(characterId);
// omit validation for brevity
return View("Sheet", _viewModelFactory.CreateNew<ViewModel>(character, true, true));
}

并且在单元测试中执行时可以断言所需的行为

[Fact]
public void TestSheet() {
// Arrange stuff and mock setup
var factory = new Mock<IViewModelFactory>();
factory.Setup(_ => _.CreateNew<ViewModel>(It.IsAny<object[]>()))
.Returns((object[] args) =>
new ViewModel((Character)args[0], (bool)args[1], (bool)args[2])
);      
// Act
var result = _controller.Sheet(characterId) as ViewResult;
// Assert
Assert.NotNull(result);
// assert ViewModel constructor called with x arguments
factory.Verify(_ => _.CreateNew<ViewModel>(It.IsAny<Character>(), true, true), Times.AtLeastOnce());
}

最新更新