简体   繁体   中英

C# MVC Model is always valid when Unit Testing a Controller Action

I am very beginner in this C# world.

There is this model class:

    public class Fund
    {
        [Required]
        public int id { get; set; }
        [Required]
        public string name { get; set; }
        [Required]
        public string nickname { get; set; }
    }

Although the [Required] annotation is over all properties, an instance like:

Fund f = new Fund();
f.name = "test name";
f.nickname = "test ninckname";

always pass in a test like:

if (ModelState.IsValid)
{
    // do stuff
}

How am I supposed to set the model such that an instance like that won't pass in in the ModelState.IsValid test?

Other instances like:

Fund f1 = new Fund();
f1.id = 3;
f1.nickname = "test ninckname";

and

Fund f2 = new Fund();
f2.id = 3;
f2.name = "test name";

are also passing on the test.

EDIT:

The ModelState.IsValid is inside a controller, I am testing the controller actually.

EDIT 2:

That is the controller's method signature:

[HttpPatch]
public ActionResult EditFund(Fund fund)

EDIT 3: That is my whole test method:

[TestMethod]
public void TestEditInvalidFund()
{
    // Arrange
    FundController controller = new FundController();
    controller.ControllerContext = TestModelHelper.AdminControllerContext();
    var _fund = new Mock<IFund>();
    _fund.SetupGet(f => f.id).Returns(1);
    //_fund.SetupGet(f => f.name).Returns("Fund name");
    _fund.SetupGet(f => f.nickname).Returns("Fund nickname");
    _fund.Setup(f => f.Edit()).Callback(() => {}).Verifiable();

    // Act
    var result = (JsonResult)controller.EditFund(_fund.Object);

    // Assert
    SimpleMessage resultMessage = m_serializer.Deserialize<SimpleMessage>(m_serializer.Serialize(result.Data));
    Assert.IsNotNull(resultMessage.status, "JSON record does not contain 'status' required property.");
    Assert.IsTrue(resultMessage.status.Equals("fail"), "status must be 'fail'");
}

And that is the whole controller's method:

[HttpPatch]
public ActionResult EditFund(IFund _fund)
{
    try
    {
        if (ModelState.IsValid)
        {
            //_fund.Edit();
        }
        else
        {
            string error_messages = "";
            foreach (var e in ModelState.Select(x => x.Value.Errors).Where(y => y.Count > 0).ToList())
            {
                error_messages += e[0].ErrorMessage + "\n";
            }
            throw new Exception(error_messages);
        }
        return MessageHelper(@Resources.Global.success, @Resources.Funds.success_editing_fund, "success");
    }
    catch (Exception err)
    {
        return ErrorHelper(@Resources.Global.error, @Resources.Funds.error_editing_fund, err.Message);
    }
}
[Required]
public int id { get; set; }

This will always be valid since an int is not nullable. It has a default value of 0.

You can't unit test ModelState.IsValid . Well, you can, but you need extra code to do it, and it's not exactly ideal.

Here's my code on github: WithModelStateIsInvalid<TController>()

And here's the NuGet package it's in: https://www.nuget.org/packages/TestBase-Mvc/

And here's how I use it:

UnitUnderTest
    .WithModelStateIsInValid()
    .Action(model)
    .ShouldBeViewResult()
    .ShouldBeAnInvalidModel();

—————————————————————————————————————————

The reason you can't unit test it, is that Model Validation happens in the ModelBinding step before your controller action is called.

So to simulate a model validation failure, the easiest way is to invalidate controller.ModelState 'manually' in your unit test code, before calling the action. Which is what the extension method does, but actually it's just one-line:

controller.ModelState.AddModelError("FieldName", @"error message")

(A more sophisticated extension method would probably let you specify which Model key is invalid. PRs always welcome).

不确定为什么有效,但是您可以尝试通过以下方式显式地重新验证模型:

TryValidateModel(fund)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM