- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
TryUpdateModelAsync 是抽象类 PageModel 的一个内部保护方法,在我看来,这使得它不可模拟。是否有任何方法可以对涉及此方法的任何操作进行单元测试?
我在网上看到这个解决方案: asp.net core mvc controller unit testing when using TryUpdateModel .
但是,它只适用于 ASP.NET Core MVC Web App,因为 TryUpdateModelAsync 是一个公共(public)方法,所以我们可以添加一个适配器来包装这个方法,让我们的适配器调用实际的方法。在 Razor 页面中,无法从外部访问此 TryUpdateModelAsync。
我现在遇到的问题是当我对 OnPostAsync 方法进行单元测试时,TryUpdateModelAsync
总是抛出异常。我永远无法通过那条线来检查它之后的代码逻辑。
所以这是我要进行单元测试的操作:
public class CreateModel : PageModel
{
//elided
[BindProperty]
public Post Post { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (await TryUpdateModelAsync<Post>(Post, "Post", p => p.Title, p => p.SubTitle, p => p.Author, p => p.Content, p => p.CategoryID)){
//do stuff
return RedirectToPage("./Index");
}
//do stuff
return Page();
}
这是我为它编写的单元测试:
[Test]
public async Task InvokeOnPostAsyncWithInvalidModelState_ShouldReturnPageResultType(){
//Arrange
InitPostModelProperties();
var createModel = new CreateModel(){
PageContext = _pageContext,
TempData = _tempData,
Url = _urlHelper
};
//do arrangement
//Act
var result = await createModel.OnPostAsync();
//Assert
}
更新:
下面是我为这个测试所做的完整初始化过程:
public void InitPostModelProperties(){
_httpContext = new DefaultHttpContext(){
//RequestServices = services.BuildServiceProvider()
};
_modelStateDictionary = new ModelStateDictionary();
_modelMetaDataProvider = new EmptyModelMetadataProvider();
_actionContext = new ActionContext(_httpContext, new RouteData(), new PageActionDescriptor(), _modelStateDictionary);
_viewData = new ViewDataDictionary(_modelMetaDataProvider, _modelStateDictionary);
_tempData = new TempDataDictionary(_httpContext, Mock.Of<ITempDataProvider>());
_pageContext = new PageContext(_actionContext){
ViewData = _viewData
};
_urlHelper = new UrlHelper(_actionContext);
}
然后我收到以下异常消息:
Error Message:
System.ArgumentNullException : Value cannot be null.
Parameter name: metadataProvider
Stack Trace:
at Microsoft.AspNetCore.Mvc.ModelBinding.Internal.ModelBindingHelper.TryUpdateModelAsync(Object model, Type modelType, String prefix, ActionContext actionContext, IModelMetadataProvider metadataProvider, IModelBinderFactory modelBinderFactory, IValueProvider valueProvider, IObjectModelValidator objectModelValidator, Func`2 propertyFilter)
at Microsoft.AspNetCore.Mvc.RazorPages.PageModel.TryUpdateModelAsync[TModel](TModel model, String name, Expression`1[] includeExpressions)
然后我将初始化更改为以下内容:
public void InitPostModelProperties(){
var services = new ServiceCollection();
services.AddTransient<IModelMetadataProvider, ModelMetadataProvider>();
_httpContext = new DefaultHttpContext(){
RequestServices = services.BuildServiceProvider()
};
_modelStateDictionary = new ModelStateDictionary();
_modelMetaDataProvider = new EmptyModelMetadataProvider();
_actionContext = new ActionContext(_httpContext, new RouteData(), new PageActionDescriptor(), _modelStateDictionary);
_viewData = new ViewDataDictionary(_modelMetaDataProvider, _modelStateDictionary);
_tempData = new TempDataDictionary(_httpContext, Mock.Of<ITempDataProvider>());
_pageContext = new PageContext(_actionContext){
ViewData = _viewData
};
_urlHelper = new UrlHelper(_actionContext);
}
然后我得到了新的异常信息:
Outcome: Failed
Error Message:
System.InvalidOperationException : No service for type 'Microsoft.AspNetCore.Mvc.ModelBinding.IModelMetadataProvider' has been registered.
Stack Trace:
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.AspNetCore.Mvc.RazorPages.PageModel.get_MetadataProvider()
at Microsoft.AspNetCore.Mvc.RazorPages.PageModel.TryUpdateModelAsync[TModel](TModel model, String name, Expression`1[] includeExpressions)
我按照异常消息添加了这些行:
services.AddSingleton<IModelMetadataProvider, EmptyModelMetadataProvider>();
收到新的异常消息:
Outcome: Failed
Error Message:
System.InvalidOperationException : No service for type 'Microsoft.AspNetCore.Mvc.ModelBinding.IModelBinderFactory' has been registered.
Stack Trace:
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.AspNetCore.Mvc.RazorPages.PageModel.get_ModelBinderFactory()
at Microsoft.AspNetCore.Mvc.RazorPages.PageModel.TryUpdateModelAsync[TModel](TModel model, String name, Expression`1[] includeExpressions)
我一直在添加新行:
services.AddSingleton<IModelBinderFactory,ModelBinderFactory>();
才发现服务无法实例化,因为ModelBinderFactory类是抽象类
我被困在这里了。
更新
整个测试代码如下:
源代码:
public class BaseTests
{
private HugoBlogContext _context;
public HugoBlogContext Context => _context;
public List<Category> CatList;
public List<Post> PostList;
public List<Tag> TagList;
public List<PostTag> PostTagList;
public BaseTests(){}
public class PageModelBaseTests: BaseTests
{
protected ICategoryRepository _categoryRepository;
protected IPostRepository _postRepository;
protected IPostTagRepository _postTagRepository;
protected ITagRepository _tagRepository;
protected ISelectTagService _selectTagService;
protected IDropdownCategoryService _dropdownCategoryService;
protected HttpContext _httpContext;
protected ModelStateDictionary _modelStateDictionary;
protected ActionContext _actionContext;
protected ModelMetadataProvider _modelMetaDataProvider;
protected ViewDataDictionary _viewData;
protected TempDataDictionary _tempData;
protected PageContext _pageContext;
protected UrlHelper _urlHelper;
[SetUp]
public void Init(){
_categoryRepository = new CategoryRepository(Context);
_postRepository = new PostRepository(Context);
_postTagRepository = new PostTagRepository(Context);
_tagRepository = new TagRepository(Context);
_selectTagService = new SelectTagService(_tagRepository, _postRepository, _postTagRepository);
_dropdownCategoryService = new DropdownCategoryService(_categoryRepository);
}
public void InitPostModelProperties(){
var services = new ServiceCollection();
services.AddSingleton<IModelMetadataProvider, EmptyModelMetadataProvider>();
services.AddSingleton<IModelBinderFactory,ModelBinderFactory>();
_httpContext = new DefaultHttpContext(){
RequestServices = services.BuildServiceProvider()
};
_modelStateDictionary = new ModelStateDictionary();
_modelMetaDataProvider = new EmptyModelMetadataProvider();
_actionContext = new ActionContext(_httpContext, new RouteData(), new PageActionDescriptor(), _modelStateDictionary);
_viewData = new ViewDataDictionary(_modelMetaDataProvider, _modelStateDictionary);
_tempData = new TempDataDictionary(_httpContext, Mock.Of<ITempDataProvider>());
_pageContext = new PageContext(_actionContext){
ViewData = _viewData
};
_urlHelper = new UrlHelper(_actionContext);
}
[TestFixture]
public class CreateModelTests: PageModelBaseTests
{
[Test]
public async Task InvokeOnPostAsyncWithValidModelState_ShouldReturnRedirectPageResultTypeAndCategoryShouldBeUpdated(){
//Arrange
InitPostModelProperties();
var createModel = new CreateModel(_selectTagService, _dropdownCategoryService ,_postRepository){
PageContext = _pageContext,
TempData = _tempData,
Url = _urlHelper,
};
createModel.Post = new Post{
Title = "Create",
SubTitle = "Create",
Content = "Create",
Author = "Create",
CategoryID = CatList.Count + 1,
};
var selectedTags = new string[3]{"4","5","6"};
//Act
var result = await createModel.OnPostAsync(selectedTags);
//Assert
result.Should().BeOfType<RedirectToPageResult>();
Context.Posts.Where(c => c.CategoryID == (CatList.Count + 1)).Should().NotBeNull();
}
}
更新模拟后,我收到以下异常消息:
Outcome: Failed
Error Message:
System.NullReferenceException : Object reference not set to an instance of an object.
Stack Trace:
at Microsoft.AspNetCore.Mvc.ModelBinding.Internal.ModelBindingHelper.TryUpdateModelAsync(Object model, Type modelType, String prefix, ActionContext actionContext, IModelMetadataProvider metadataProvider, IModelBinderFactory modelBinderFactory, IValueProvider valueProvider, IObjectModelValidator objectModelValidator, Func`2 propertyFilter)
at Microsoft.AspNetCore.Mvc.RazorPages.PageModel.TryUpdateModelAsync[TModel](TModel model, String name, Expression`1[] includeExpressions)
仅供引用:我还 mock 了IObjectValidator
通过使用 services.AddSingleton<IObjectValidator>(Mock.Of<IObjectValidator>())
最佳答案
我不确定测试 TryValidateModel
或 TryUpdateModelAsync
是我们的责任;可以安全地假设框架方法是正确的,对吧。
另一个论点是您正在测试您的实现而不是其他人的。
无论如何,如果你想忽略这些方法,你可以在你的测试方法中尝试这个(我正在使用 Moq 作为我的模拟框架)完全忽略这些方法并且实际上假设它们是正确的:
var objectModelValidatorMock = new Mock<IObjectModelValidator>();
objectModelValidatorMock
.Setup(o => o.Validate(
It.IsAny<ActionContext>(),
It.IsAny<ValidationStateDictionary>(),
It.IsAny<string>(),
It.IsAny<object>()));
var sut = new NewController
{
ObjectValidator = objectModelValidatorMock.Object
};
关于c# - 如何使用 ASP.NET CORE Razor Page Web App 中涉及的 TryUpdateModelAsync 对方法进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57641311/
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!