Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TryValidateModel in asp.net core throws Null Reference Exception while performing unit test

I'm trying to write unit tests for ModelState validation for an Asp.Net Core Web API.

I read that, the best way to do so is to use TryValidateModel function. But, every time I run the unit test, it throws NullReference exception.
I found many articles suggesting controller.ModelState.AddModelError("",""), but I'm not interested in this, as I believe that it beats the actual purpose of the real model validation.

[TestMethod]
public void TestMethod1()
{
    var controller = new TestController();

    controller.Post(new Model());
}


public class TestController : Controller
{
    public IActionResult Post(Model model)
    {
        bool b = TryValidateModel(model)

        return Ok();
    }
}

TryValidateModel(model) always throws NullReference Exception from TryValidateModel(model, prefix) function.

Appreciate any help.

like image 220
Alen Alex Avatar asked Aug 09 '18 18:08

Alen Alex


3 Answers

It's configuration/integration issue.

You can see some additional info in the issue in ASP.NET Core repo and another one on github. But I can tell you the easiest fix (I used it once)

        var objectValidator = new Mock<IObjectModelValidator>();
        objectValidator.Setup(o => o.Validate(It.IsAny<ActionContext>(), 
                                          It.IsAny<ValidationStateDictionary>(), 
                                          It.IsAny<string>(), 
                                          It.IsAny<Object>()));
        controller.ObjectValidator = objectValidator.Object;
like image 159
Egorikas Avatar answered Nov 18 '22 09:11

Egorikas


As I figured how to fix Null Reference Exception thanks to @Egorikas, I noticed that it still doesn't actually validate the model and always returns a true.

I found that we could just use Validator class in System.ComponentModel.DataAnnotationsnamespace.

[TestMethod]
public void TestMethod1()
{
    var model = new Person();
    var validationResultList = new List<ValidationResult>();


    bool b1 = Validator.TryValidateObject(model, new ValidationContext(model), validationResultList);
}

You can directly validate it from the Test method itself, rather than having to call the controller, if ModelState validation is your intention.

Hope this helps.

like image 4
Alen Alex Avatar answered Nov 18 '22 10:11

Alen Alex


Based on Andrew Van Den Brink answer's, but with actually having the validation errors set in the ModelState.

private class ObjectValidator : IObjectModelValidator
{

    public void Validate(ActionContext actionContext, ValidationStateDictionary validationState, string prefix, object model)
    {
        var context = new ValidationContext(model, serviceProvider: null, items: null);
        var results = new List<ValidationResult>();

        bool isValid = Validator.TryValidateObject(
            model, context, results,
            validateAllProperties: true
        );

        if (!isValid)
            results.ForEach((r) =>
            {
                // Add validation errors to the ModelState
                actionContext.ModelState.AddModelError("", r.ErrorMessage);
            });
    }
}

Then, simply set the ObjectValidator in your controller:

controller.ObjectValidator = new ObjectValidator();
like image 3
jBelanger Avatar answered Nov 18 '22 09:11

jBelanger