如何解决使用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);
...
如果看一下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,};
...
,
在您的帮助下,我通过模拟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 举报,一经查实,本站将立刻删除。