gpt4 book ai didi

c# - 如何使用 ASP.NET CORE Razor Page Web App 中涉及的 TryUpdateModelAsync 对方法进行单元测试?

转载 作者:行者123 更新时间:2023-12-05 07:20:03 26 4
gpt4 key购买 nike

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>())

最佳答案

我不确定测试 TryValidateModelTryUpdateModelAsync 是我们的责任;可以安全地假设框架方法是正确的,对吧。

另一个论点是您正在测试您的实现而不是其他人的。

无论如何,如果你想忽略这些方法,你可以在你的测试方法中尝试这个(我正在使用 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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com