Unit test controller model validation on AspNetCore

10,262

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

Share:
10,262

Related videos on Youtube

Andras Gaal
Author by

Andras Gaal

Updated on September 14, 2022

Comments

  • Andras Gaal
    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 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?

  • Andras Gaal
    Andras Gaal about 7 years
    Thanks, this is exactly I was looking for!
  • Khub
    Khub almost 6 years
    It's nice of you to mention TestServer and its integration testing capabilities, but the question was about a problem with unit testing.