ASP.NET Core Web API控制器单元测试编写及问题咨询
Hey there! I totally get how overwhelming unit testing can feel when you're just starting out—let's break down each of your questions with clear explanations and code examples to get you on track.
1. Should I Mock Entity Framework?
Absolutely, but not EF Core directly. Here's the reasoning:
Your OrdersController depends on the IOrderRepository interface, not the concrete OrderRepository that uses EF Core. The whole point of unit testing the controller is to validate only your controller's logic—Microsoft has already thoroughly tested EF Core's functionality, so we don't need to repeat that work.
By mocking IOrderRepository, you isolate the controller from external dependencies like databases. This makes your tests faster, more reliable, and focused solely on the code you wrote.
Example using Moq (a popular mocking library):
// Arrange var mockOrderRepo = new Mock<IOrderRepository>(); var mockMapper = new Mock<IMapper>(); var controller = new OrdersController(mockOrderRepo.Object, mockMapper.Object); // Set up the mock to return a specific value when AddOrder is called mockOrderRepo.Setup(repo => repo.AddOrder(It.IsAny<Order>())) .ReturnsAsync(new Order { Id = 1 }); // Act & Assert steps follow...
2. How to Fix OkNegotiatedContentResult Not Handling Task?
The issue here is that your controller's AddOrder method is asynchronous (returns Task<IActionResult>), so you need to await the result before trying to cast it to OkNegotiatedContentResult (or OkObjectResult, the preferred type in newer ASP.NET Core versions).
Also, make sure your test method is marked as async Task (never void for async tests).
Example fix:
// Wrong approach (doesn't await the Task) // var result = controller.AddOrder(orderDto); // Correct approach [Fact] public async Task AddOrder_ValidDto_ReturnsOkResult() { // Arrange (set up mocks as before) var orderDto = new OrderDto { /* Populate valid properties */ }; mockMapper.Setup(m => m.Map<Order>(orderDto)).Returns(new Order()); // Act var actionResult = await controller.AddOrder(orderDto); var okResult = actionResult as OkObjectResult; // Use OkNegotiatedContentResult for older ASP.NET Core versions // Assert Assert.NotNull(okResult); Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode); }
3. Why Aren't [Required] Attributes Being Enforced in Tests?
This happens because model validation isn't automatically triggered when calling controller methods directly in unit tests. You need two key steps to fix this:
Step 1: Ensure Your Controller Checks Model State
First, update your AddOrder method to validate the model state before proceeding:
[HttpPost] public async Task<IActionResult> AddOrder([FromBody] OrderDto orderDto) { if (!ModelState.IsValid) { return BadRequest(ModelState); // Return 400 if validation fails } // Rest of your order processing logic... }
Step 2: Trigger Validation in Your Test
In your test, after passing an invalid DTO (missing required fields), manually trigger model validation to ensure the controller behaves as expected:
[Fact] public async Task AddOrder_MissingRequiredFields_ReturnsBadRequest() { // Arrange var invalidOrderDto = new OrderDto(); // Missing fields marked [Required] var controller = new OrdersController(mockOrderRepo.Object, mockMapper.Object); // Trigger model validation for the invalid DTO controller.TryValidateModel(invalidOrderDto); // Act var actionResult = await controller.AddOrder(invalidOrderDto); var badRequestResult = actionResult as BadRequestObjectResult; // Assert Assert.NotNull(badRequestResult); Assert.Equal(StatusCodes.Status400BadRequest, badRequestResult.StatusCode); Assert.False(controller.ModelState.IsValid); }
If you need more control, you can also manually add model errors:
controller.ModelState.AddModelError("OrderNumber", "The OrderNumber field is required.");
内容的提问来源于stack exchange,提问作者Tushar Goel

