C# 使用TryUpdateModel时asp.net核心mvc控制器单元测试
在asp.net核心应用程序中,我有一对响应编辑操作的控制器方法。一个用于GET,它为实体id接受一个字符串参数:C# 使用TryUpdateModel时asp.net核心mvc控制器单元测试,c#,unit-testing,asp.net-core,asp.net-core-mvc,C#,Unit Testing,Asp.net Core,Asp.net Core Mvc,在asp.net核心应用程序中,我有一对响应编辑操作的控制器方法。一个用于GET,它为实体id接受一个字符串参数: public async Task<IActionResult> Edit(string id) 这很好 现在,我正试图为此编写一个测试,但我发现tryupdatemodelsync需要从HttpContext中获得很多东西,并且需要充实控制器。我已经尝试过模仿这些,但是在查看了tryupdatemodelsync的源代码之后,我意识到我基本上需要模仿所有内容,直到元
public async Task<IActionResult> Edit(string id)
这很好
现在,我正试图为此编写一个测试,但我发现tryupdatemodelsync
需要从HttpContext
中获得很多东西,并且需要充实控制器。我已经尝试过模仿这些,但是在查看了tryupdatemodelsync
的源代码之后,我意识到我基本上需要模仿所有内容,直到元数据,这并不简单
我想知道这个困难是否告诉了我一些事情:tryupdatemodelsync
使测试变得困难,所以我应该重构控制器方法,使其不依赖于这个助手。相反,我可以为viewmodel的方法添加另一个参数,并用[FromBody]
对其进行修饰,这样模型绑定将在post字段出现时发生,但在测试时我可以传入视图模型。但是,我喜欢tryupdatemodelsync
方法,因为它可以将post字段合并到我的视图模型中。我认为实现合并的另一种方法是编写自己的合并方法。好的,没什么大不了的,但是我不想对每个实体都这样做(或者重新发明轮子来编写基于反射的合并),而且真的,我想知道我是否错过了如何编写针对此的单元测试的机会。我可以启动一个完整的TestServer
,就像我为集成测试所做的那样,但我不确定这是正确的方向,我觉得这会使我的单元测试进一步复杂化。然而,在这种情况下,这可能是合理的吗
我已经看到了以前版本的.net mvc的答案,他们所需要做的只是模拟一个IValueProvider
,并将其连接到控制器,但在.net core中,tryupdatemodelsync
似乎已被修改,需要更多移动部件
总而言之,我认为有三种选择:
tryupdatemodelsync
需要。看来这可能是一条死胡同
到目前为止TestServer
从一点开始进行此测试
使用HttpClient
[FromBody]
在视图模型参数上,然后编写我自己的合并
每个实体的方法,按单步执行
tryupdatemodelsync
这些都有其缺点,所以我希望这个列表中有第四个条目我看不到,因为我不知道。在查看ControllerBase的源代码后,我注意到有问题的方法依赖于static方法
ModelBindingHelper.tryupdatemodelsync
(说真的!!!!?我还以为我们现在进化得更快了呢。)
正如您已经痛苦地发现的那样,这使得测试您的控制器有些麻烦
我想知道这个困难是否告诉了我一些事情
好吧,别再疑惑了。是的
这里是另一个选项,你可能已经看得太多了。抽象/适应困难,使之回到它产生的深处
public interface IModelBindingHelperAdaptor {
Task<bool> TryUpdateModelAsync<TModel>(ControllerBase controller, TModel model) where TModel : class;
}
您现在可以自由地模拟您的抽象,而无需所有的紧密耦合,这是我认为他们首先应该做的
现在,我假设您已经知道如何在启动中设置必要的内容,以允许上述建议发挥作用,因此启动并运行该建议对您来说应该不是一项困难的任务。查看ControllerBase的源代码后,我注意到有问题的麻烦方法依赖于static方法
ModelBindingHelper.tryupdatemodelsync
(说真的!!!!?我还以为我们现在进化得更多呢。)
正如您已经痛苦地发现的那样,这使得测试您的控制器有些麻烦
我想知道这个困难是否告诉了我一些事情
我们别再疑惑了。它是……)
这里是另一个选项,你可能已经看得太多了。抽象/适应困难,使之回到它产生的深处
public interface IModelBindingHelperAdaptor {
Task<bool> TryUpdateModelAsync<TModel>(ControllerBase controller, TModel model) where TModel : class;
}
您现在可以自由地模拟您的抽象,而无需所有的紧密耦合,这是我认为他们首先应该做的
现在,我假设您已经知道如何在初创公司中设置必要的东西,以使上述建议发挥作用,因此对您来说,启动并运行它应该不是一项困难的任务。好吧,我喜欢这个解决方案-如果您需要模拟它,请将其包装并注入。我同意,ControllerBase没有很好地遵循显式依赖原则。我走了另一个方向,充实了HttpContext,遇到了一个问题,它没有抛出异常,但是模型没有更新。我将把这个留给另一个不同的问题。这似乎提供了一个可行的第四种选择:)好吧,我喜欢这个解决方案——如果您需要模拟它,请将其包装并注入。我同意,ControllerBase没有很好地遵循显式依赖原则。我走了另一个方向,充实了HttpContext,遇到了一个问题,它没有抛出异常,但是模型没有更新。我将把这个留给另一个不同的问题。这似乎提供了一个可行的第四种选择:)
public interface IModelBindingHelperAdaptor {
Task<bool> TryUpdateModelAsync<TModel>(ControllerBase controller, TModel model) where TModel : class;
}
public class DefaultModelBindingHelperAdaptor : IModelBindingHelperAdaptor {
public virtual Task<bool> TryUpdateModelAsync<TModel>(ControllerBase controller, TModel model) where TModel : class {
return controller.TryUpdateModelAsync(model);
}
}
var bindingSuccess = await modelBindingHelper.TryUpdateModelAsync(this, vm);