18

In an ASPNET Core project I am trying to create some unit tests that would verify my data validation logic works fine.

My controller is very simple:

[HttpPost]
[Route("Track")]
public void Track([FromBody] DataItem item)
{
    if (!ModelState.IsValid) throw new ArgumentException("Bad request");

    _dataItemSaver.SaveData(item);
}

I am using a test base class that would set up the _myController object as the controller under test.

    public ControllerTestBase()
    {
        var builder = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"buildversion.json", optional: true)
            .AddEnvironmentVariables();
        _config = builder.Build();

        var services = new ServiceCollection()
            .AddEntityFrameworkInMemoryDatabase()
            .AddDbContext<MyDbContext>(options =>
            {
                options.UseInMemoryDatabase();
            })
            .AddScoped<IDataItemSaver, DataItemSQLStorageService>()
            .AddScoped<MyController>()
            .Configure<MyConfig>(_config.GetSection(nameof(MyConfig)));

        services
            .AddMvc(mvcOptions =>
                {
                    mvcOptions.Filters.AddService(typeof(GlobalExceptionFilter), 0);
                });

        _additionalDISetupActions?.Invoke(services);

        _serviceProvider = services.BuildServiceProvider();

        _myController = _serviceProvider.GetService<MyController>();
    }

The test again is very simple:

    [TestMethod]
    public void Prop3Required()
    {
        // Arrange
        var dataItem = new DataItem()
        {
            Prop1 = "Prop1",
            Prop2 = "Prop2"
        };

        // Act & Assert
        Assert.ThrowsException<ArgumentException>(() => _myController.Track(dataItem));
    }

I am finding though that ModelState.IsValid is true when running a unittest even when my DataItem is missing required attributes (Prop3 in this example). When testing the controller through the web with the same input, the validation works correctly (returning false for ModelState.IsValid).

How do I properly trigger the ASPNET Core logic for modelstate validation from a unit test?

4
  • 1
    Your not really going to throw an exception when ModelState is invalid are you? Commented Apr 5, 2017 at 11:05
  • I did, and handled the exception in a global exception filter to keep all error handling and formatting in the same place. Could move the check to a filter and apply globally but would keep the way to throw the exception and handle it in my global filter. Commented Apr 5, 2017 at 11:07
  • Seriously? Your method should be returning the view so the user can correct validation errors. Commented Apr 5, 2017 at 11:09
  • I did not mention that this is an API method. There's a designated error type (ApiError) that will contain modelstate errors. The ApiError object is assembled in my global exception filter based on the type of error encountered. Commented Apr 5, 2017 at 11:14

2 Answers 2

25

You should take a look at Integration Testing with ASP.NET Core (https://learn.microsoft.com/en-us/aspnet/core/testing/integration-testing), it is a very simple way to host your application in a test context and test your entire pipeline.
As explained in the documentation you could do something like this in your test method:

_server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
_client = _server.CreateClient();
// Pass a not valid model 
var response = await _client.PostAsJsonAsync("Track", new DataItem());
Assert.IsFalse(response.IsSuccessStatusCode);
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, this is exactly I was looking for!
It's nice of you to mention TestServer and its integration testing capabilities, but the question was about a problem with unit testing.
20

If you want to do a pure unit test you need to manually simulate the model state error because the model state validation is only triggered during runtime.

_myController.ModelState.AddModelError("yourItemFieldHere", "Your validation type here");

See https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/testing for more detail

1 Comment

Problem with this approach is that the test would still pass regardless of the presence of the Required data annotation attribute on the properties in the DataItem model.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.