I have what seems to be a very similar issue to issue #3586, where TryValidateModel throws an Object reference error when unit tested. During normal operation with similar data being passed into the controller, the validation passes. Here is the controller method:
public class LocationsController : Controller
{
[HttpPut("{id}")]
public IActionResult PartiallyUpdateLocation(int id,
[FromBody] JsonPatchDocument<LocationForUpdateDTO> patchDoc)
{
if (patchDoc == null)
{
return BadRequest();
}
var locationEntity = _locationsRepository.GetLocation(id);
if (locationEntity == null)
{
return NotFound();
}
var locationToPatch = Mapper.Map<LocationForUpdateDTO>(locationEntity);
patchDoc.ApplyTo(locationToPatch, ModelState);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
**FAILS HERE**
TryValidateModel(locationToPatch);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var finalLocation = Mapper.Map(locationToPatch, locationEntity);
_locationsRepository.PartiallyUpdate(id, finalLocation);
return NoContent();
}
}
...and here is the unit test implementation:
[Collection("AutoMapper collection")]
public class LocationsController_PatchLocationTests
{
AutoMapperConfigFixture fixture;
public LocationsController_PatchLocationTests(AutoMapperConfigFixture fixture)
{
this.fixture = fixture;
}
[Fact]
public void PatchLocationReturnsNoContent()
{
var repositoryMock = GetMockRepositoryHelper(1);
LocationsController controller = new LocationsController(repositoryMock);
var patchDoc = new JsonPatchDocument<LocationForUpdateDTO>();
patchDoc.Replace(l => l.Street, TestDataForPartialUpdate.goodLocation.Street);
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Location, LocationForUpdateDTO>();
});
var result = controller.PartiallyUpdateLocation(1, patchDoc);
Assert.IsType(typeof(NoContentResult), result);
}
private static ILocationsRepository GetMockRepositoryHelper(int id)
{
Mock<ILocationsRepository> repositoryMock = new Mock<ILocationsRepository>();
repositoryMock.Setup(x => x.GetLocation(id))
.Returns(
new Location()
{
Id = 1,
Street = "123 AppleTree Way",
City = "Galloway",
State = "Ohio",
ZipCode = "43119",
Latitude = 40.0,
Longitude = 80.0
});
return repositoryMock.Object;
}
}
As well as the Automapper CollectionDefinition:
[CollectionDefinition("AutoMapper collection")]
public class AutoMapperConfig : ICollectionFixture<AutoMapperConfigFixture>
{
}
The stack trace:
at Microsoft.AspNetCore.Mvc.ControllerBase.TryValidateModel(Object model, String prefix)
at LocationsAPI.Controllers.LocationsController.PartiallyUpdateLocation(Int32 id, JsonPatchDocument`1 patchDoc) in editedpath\src\LocationsAPI\Controllers\LocationsController.cs:line 138
at LocationsAPI.Tests.Unit.LocationsController_PatchLocationTests.PatchLocationReturnsNoContent() in editedpath\test\LocationsAPI.Tests.Unit\LocationsController_PatchLocationTests.cs:line 36
You'll need to set controller.ObjectValidator
when trying to test anything that calls TryValidateModel
. I wonder if there's anything we can do to make this better.
Closing because this appears to be answered. If this doesn't help please reactivate or open a new issue
@rynowak I was just having the same issue. while search for answer land me on this page. even adding the code below solve the problem. But it does not feel right that we need to do that in order to use TryValidateModel for unit testing.
and this knowledge (or information) is not mentioned in the document of ControllerBase.TryValidateModel
It does not feel right because this piece of code is not within any of our testing logic. If we cannot improve upon this, we should at least mention this in the docs of TryValidateModel.
var objectValidator = new Mock<IObjectModelValidator>();
objectValidator.Setup(o => o.Validate(It.IsAny<ActionContext>(),
It.IsAny<ValidationStateDictionary>(),
It.IsAny<string>(),
It.IsAny<Object>()));
_sut.ObjectValidator = objectValidator.Object;
Most helpful comment
@rynowak I was just having the same issue. while search for answer land me on this page. even adding the code below solve the problem. But it does not feel right that we need to do that in order to use TryValidateModel for unit testing.
and this knowledge (or information) is not mentioned in the document of ControllerBase.TryValidateModel
It does not feel right because this piece of code is not within any of our testing logic. If we cannot improve upon this, we should at least mention this in the docs of TryValidateModel.
var objectValidator = new Mock<IObjectModelValidator>(); objectValidator.Setup(o => o.Validate(It.IsAny<ActionContext>(), It.IsAny<ValidationStateDictionary>(), It.IsAny<string>(), It.IsAny<Object>())); _sut.ObjectValidator = objectValidator.Object;