如何使用xUnit与Moq编写ProductController的Delete动作单元测试?
ProductController Delete动作的单元测试方案(xUnit + Moq)
一、GET /Product/Delete/{id} 的测试场景与代码
该动作负责返回删除确认页面,需验证以下场景:
- 传入存在的产品ID时,返回包含对应Product对象的View
- 传入不存在的产品ID时,返回包含null的View
测试代码示例:
using Xunit; using Moq; using YourNamespace.Controllers; using YourNamespace.Services; using YourNamespace.Models; public class ProductControllerTests { private readonly Mock<IProductService> _mockProductService; private readonly ProductController _controller; public ProductControllerTests() { _mockProductService = new Mock<IProductService>(); _controller = new ProductController(_mockProductService.Object); } [Fact] public void Delete_Get_ReturnsViewWithProduct_WhenIdExists() { // 准备测试数据 var testProduct = new Product { Id = 1, Name = "Test Product" }; _mockProductService.Setup(s => s.GetById(1)).Returns(testProduct); // 执行动作 var result = _controller.Delete(1); // 断言结果 var viewResult = Assert.IsType<ViewResult>(result); Assert.Equal(testProduct, viewResult.Model); _mockProductService.Verify(s => s.GetById(1), Times.Once); } [Fact] public void Delete_Get_ReturnsViewWithNull_WhenIdDoesNotExist() { // 准备:模拟不存在ID的返回值 _mockProductService.Setup(s => s.GetById(999)).Returns((Product)null); // 执行动作 var result = _controller.Delete(999); // 断言结果 var viewResult = Assert.IsType<ViewResult>(result); Assert.Null(viewResult.Model); _mockProductService.Verify(s => s.GetById(999), Times.Once); } }
二、POST /Product/Delete/{id} 的测试场景与代码
该动作负责执行实际删除逻辑,需验证以下场景:
- 删除成功时,重定向到Index页面
- 删除抛出异常时,返回错误View
测试代码示例:
[Fact] public void Delete_Post_RedirectsToIndex_WhenDeleteSucceeds() { // 准备:模拟删除成功的行为 _mockProductService.Setup(s => s.Remove(1)).Verifiable(); // 执行动作 var result = _controller.Delete(1, null); // 断言结果 var redirectResult = Assert.IsType<RedirectToActionResult>(result); Assert.Equal(nameof(ProductController.Index), redirectResult.ActionName); _mockProductService.Verify(s => s.Remove(1), Times.Once); } [Fact] public void Delete_Post_ReturnsView_WhenDeleteThrowsException() { // 准备:模拟删除抛出异常 _mockProductService.Setup(s => s.Remove(1)).Throws(new Exception("Delete failed")); // 执行动作 var result = _controller.Delete(1, null); // 断言结果 Assert.IsType<ViewResult>(result); _mockProductService.Verify(s => s.Remove(1), Times.Once); }
额外:ProductService.Remove方法的集成测试
如果需要验证服务层的删除逻辑(直接操作DbContext),可使用EF Core内存数据库编写集成测试:
using Xunit; using Microsoft.EntityFrameworkCore; using YourNamespace.Services; using YourNamespace.Models; public class ProductServiceTests { [Fact] public void Remove_DeletesProduct_FromDatabase() { // 准备内存数据库 var options = new DbContextOptionsBuilder<DataBaseContext>() .UseInMemoryDatabase(databaseName: "Test_Delete_Product") .Options; // 初始化测试数据 using (var context = new DataBaseContext(options)) { context.Products.Add(new Product { Id = 1, Name = "Test Product" }); context.SaveChanges(); } // 执行删除并验证 using (var context = new DataBaseContext(options)) { var service = new ProductService(context); service.Remove(1); var deletedProduct = context.Products.Find(1); Assert.Null(deletedProduct); } } [Fact] public void Remove_ThrowsException_WhenProductNotFound() { // 准备空的内存数据库 var options = new DbContextOptionsBuilder<DataBaseContext>() .UseInMemoryDatabase(databaseName: "Test_Delete_NonExistent_Product") .Options; using (var context = new DataBaseContext(options)) { var service = new ProductService(context); // 断言删除不存在的产品会抛出异常 Assert.Throws<InvalidOperationException>(() => service.Remove(999)); } } }
内容的提问来源于stack exchange,提问作者Najmeh Sh
相关产品推荐
相关产品推荐

