Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

TryValidateModel throws exception when unit tested #6000

Closed
michaelbowman1024-zz opened this issue Mar 21, 2017 · 3 comments
Closed

TryValidateModel throws exception when unit tested #6000

michaelbowman1024-zz opened this issue Mar 21, 2017 · 3 comments
Labels

Comments

@michaelbowman1024-zz
Copy link

michaelbowman1024-zz commented Mar 21, 2017

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

@rynowak
Copy link
Member

rynowak commented Jun 19, 2017

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.

@rynowak
Copy link
Member

rynowak commented Jul 17, 2017

Closing because this appears to be answered. If this doesn't help please reactivate or open a new issue

@hanslai
Copy link

hanslai commented Nov 24, 2017

@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;

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants