Unit test controller model validation on AspNetCore
Solution 1
You should take a look at Integration Testing with ASP.NET Core (https://docs.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);
Solution 2
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://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/testing for more detail
Related videos on Youtube
Andras Gaal
Updated on September 14, 2022Comments
-
Andras Gaal over 1 year
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
istrue
when running a unittest even when myDataItem
is missing required attributes (Prop3
in this example). When testing the controller through the web with the same input, the validation works correctly (returningfalse
forModelState.IsValid
).How do I properly trigger the ASPNET Core logic for modelstate validation from a unit test?
-
Andras Gaal about 7 yearsThanks, this is exactly I was looking for!
-
Khub almost 6 yearsIt's nice of you to mention TestServer and its integration testing capabilities, but the question was about a problem with unit testing.