使用ControllerBase.ValidationProblem时.Net Core NullReferenceException

如何解决使用ControllerBase.ValidationProblem时.Net Core NullReferenceException

我正在用控制器编写用户创建方法的单元测试。 当我运行单元测试时,它在行中返回NullReferenceException return ValidationProblem();在我的控制器方法中。

[xUnit.net 00:00:01.16]     WotkTimeManager.Tests.UsersControllerTests.PostUsers_BadResult_WhenInvalidData [FAIL]
  X WotkTimeManager.Tests.UsersControllerTests.PostUsers_BadResult_WhenInvalidData [285ms]
  Error Message:
   System.NullReferenceException : Object reference not set to an instance of an object.
  Stack Trace:
     at Microsoft.AspNetCore.Mvc.ControllerBase.ValidationProblem(String detail,String instance,Nullable`1 statusCode,String title,String type,ModelStateDictionary modelStateDictionary)
   at Microsoft.AspNetCore.Mvc.ControllerBase.ValidationProblem(ModelStateDictionary modelStateDictionary)
   at Microsoft.AspNetCore.Mvc.ControllerBase.ValidationProblem()
   at WorkTimeManager.Controllers.UsersController.Post(UserCreateDto user) in /mnt/c/Users/kubw1/WorkTimeManagerSolution/src/WorkTimeManager/Controllers/UsersController.cs:line 72
   at WotkTimeManager.Tests.UsersControllerTests.PostUsers_BadResult_WhenInvalidData() in /mnt/c/Users/kubw1/WorkTimeManagerSolution/test/WotkTimeManager.Tests/UsersControllerTests.cs:line 92
--- End of stack trace from prevIoUs location where exception was thrown ---

我的控制器方法

        [HttpPost]
        public async Task<ActionResult<string>> Post(UserCreateDto user)
        {
            var usermodel = _mapper.Map<User>(user);

            var result = await _userManager.CreateAsync(usermodel,user.password);

            if (result.Succeeded)
            {
                return Ok();
            }
            else
            {
                foreach (var err in result.Errors)
                {
                    ModelState.AddModelError(err.Code,err.Description);
                }
                return ValidationProblem();
            }

        }

单元测试

        [Fact]
        public async Task PostUsers_BadResult_WhenInvalidData()
        {
            var user = new UserCreateDto
            {
                username = "test",password = "testp",email = "email@wp.pl"
            };

            userManager
                .Setup(x => x.CreateAsync(It.IsAny<User>(),It.IsAny<string>()))
                .ReturnsAsync(IdentityResult.Failed(new IdentityError { Code = "Problem",Description = "Not working" })).Verifiable();

            controller = new UsersController(new UnitOfWork(dbContext),userManager.Object,mapper);

            var result = await controller.Post(user);

            Assert.IsType<ValidationProblemDetails>(result.Result);
        }

解决方法

查看抛出的方法的source

public virtual ActionResult ValidationProblem(
    string detail = null,string instance = null,int? statusCode = null,string title = null,string type = null,[ActionResultObjectValue] ModelStateDictionary modelStateDictionary = null)
{
    modelStateDictionary ??= ModelState;

    var validationProblem = ProblemDetailsFactory.CreateValidationProblemDetails(...);

看起来可以抛出。那么ProblemDetailsFactory come from在哪里?

public ProblemDetailsFactory ProblemDetailsFactory
{
    get
    {
        if (_problemDetailsFactory == null)
        {
            _problemDetailsFactory = HttpContext?.RequestServices?.GetRequiredService<ProblemDetailsFactory>();
        }

        return _problemDetailsFactory;
    }
    set
    {
        if (value == null)
        {
            throw new ArgumentNullException(nameof(value));
        }

        _problemDetailsFactory = value;
    }
}

您没有向控制器提供HttpContext(如果您提供了ProblemDetailsFactory,则没有这样做),因此,此吸气剂确实返回了null,从而导致调用CreateValidationProblemDetails()引发NRE。

因此您需要提供它。 ASP.NET使用的DefaultProblemDetailsFactory是internal,因此最好对其进行模拟:

controller.ProblemDetailsFactory = new Mock<ProblemDetailsFactory>();

然后设置您期望的通话。

,

如果我不得不猜测,我会说[ { "name": "n1","value": "v1" },{ "name": "n2","value": "v2" },{ "name": "n3","value": "v3" } ] 可能试图访问HTTP上下文,该上下文在单元测试中不可用。您必须像这样模拟HTTP上下文:https://stackoverflow.com/a/2497618/1185136

,

就像@Rudery所说的,如果要看一下ValidationProblem的实现,则必须模拟HttpContext,因为ProblemDetailsFactory.CreateValidationProblemDetails需要创建validationProblem对象:

[NonAction]
public virtual ActionResult ValidationProblem(
    string detail = null,[ActionResultObjectValue] ModelStateDictionary modelStateDictionary = null)
{
    modelStateDictionary ??= ModelState;

    var validationProblem = ProblemDetailsFactory.CreateValidationProblemDetails(
        HttpContext,modelStateDictionary,statusCode: statusCode,title: title,type: type,detail: detail,instance: instance);

    ...

https://github.com/dotnet/aspnetcore/blob/9d7c3aff96e4bd2af7179fc3ee04e2e4a094c593/src/Mvc/Mvc.Core/src/ControllerBase.cs#L1951

如果看一下ValidationProblem的ASP.NET Core测试,您发现需要模拟ProblemDetailsFactory

[Fact]
public void ValidationProblemDetails_Works()
{
    // Arrange
    var context = new ControllerContext(new ActionContext(
        new DefaultHttpContext { TraceIdentifier = "some-trace" },new RouteData(),new ControllerActionDescriptor()));

    context.ModelState.AddModelError("key1","error1");

    var controller = new TestableController
    {
        ProblemDetailsFactory = // Mock ProblemDetailsFactory 
        ControllerContext = context,};
    ...

https://github.com/dotnet/aspnetcore/blob/116799fa709ff003781368b578e4efe2fa32e937/src/Mvc/Mvc.Core/test/ControllerBaseTest.cs#L2296

,

在您的帮助下,我通过模拟ProblemDetailsDactory,CreateValidationProblemDetails方法和HttpContext来使它正常工作。 谢谢。


            controller = new UsersController(new UnitOfWork(dbContext),userManager.Object,mapper);
            
            var ctx = new ControllerContext() { HttpContext = new DefaultHttpContext() };
            controller.ControllerContext = ctx;

            var problemDetails = new ValidationProblemDetails();
            var mock = new Mock<ProblemDetailsFactory>();
            mock
                .Setup(_ => _.CreateValidationProblemDetails(
                    It.IsAny<HttpContext>(),It.IsAny<ModelStateDictionary>(),It.IsAny<int?>(),It.IsAny<string>(),It.IsAny<string>())
                )
                .Returns(problemDetails);


            controller.ProblemDetailsFactory = mock.Object;

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?