【问题标题】:ASP.NET MVC unit testing with MOQ object使用 MOQ 对象进行 ASP.NET MVC 单元测试
【发布时间】:2013-11-12 10:32:47
【问题描述】:

在单元测试中模拟以下代码的最佳方法是什么:

public ActionResult Products()
{
      ViewBag.Title = "Company Product";                        
      IEnumerable<ProductDetailDto> productList =   ProductService.GetAllEffectiveProductDetails();
      ProductModels.ProductCategoryListModel model = new ProductModels.ProductCategoryListModel 
      {     
            //the type of ProductDetails => IEnumerable<productDetailDto>  
            ProductDetails = ProductService.GetAllEffectiveProductDetails(),

            //the type of ProductCategoryList => IEnumerable<selectlistitem>
            ProductCategoryList = productList.Select(x => new SelectListItem
            {
                Value = x.FKProductId.ToString(),
                Text = x.Name
            })
      };
      return View(model);
}

仅供参考,我正在使用 VS 2012、MVC 4.0、使用 MOQ 对象和 TFS 设置进行单元测试。

任何人都可以帮助我解决上述方法的模拟对象的最佳测试方法是什么?

【问题讨论】:

  • 我猜你可能想创建一个 Moq System.Web.HttpContextBase(根据需要设置 Moq 用户、请求、响应、会话、缓存、服务器等)。
  • 是的,我想创建 MOQ 对象。
  • 需要更多信息。您是否正在尝试为上述方法编写单元测试?而您试图模拟诸如 ProductService 之类的依赖项?您提到了“在单元测试中模拟以下代码的最佳方法是什么”。你到底想在这里模拟什么下面的代码?
  • >>是的,我正在尝试使用最小起订量编写单元测试。 >>我想知道如何在我的示例中模拟像 ProductService 这样的依赖项?

标签: unit-testing asp.net-mvc-4 controller moq


【解决方案1】:

如果你想先模拟ProductService,你需要注入这个依赖。

Constructor injection 是 ASP.NET MVC 中控制器最常用的方法。

public class YourController : Controller
{
    private readonly IProductService ProductService;

    /// <summary>
    /// Constructor injection
    /// </summary>
    public YourController(IProductService productService)
    {
        ProductService = productService;
    }

    /// <summary>
    /// Code of this method has not been changed at all.
    /// </summary>
    public ActionResult Products()
    {
        ViewBag.Title = "Company Product";
        IEnumerable<ProductDetailDto> productList = ProductService.GetAllEffectiveProductDetails();
        ProductModels.ProductCategoryListModel model = new ProductModels.ProductCategoryListModel
        {
            //the type of ProductDetails => IEnumerable<productDetailDto>  
            ProductDetails = ProductService.GetAllEffectiveProductDetails(),

            //the type of ProductCategoryList => IEnumerable<selectlistitem>
            ProductCategoryList = productList.Select(x => new SelectListItem
            {
                Value = x.FKProductId.ToString(),
                Text = x.Name
            })
        };
        return View(model);
    }
}

#region DataModels

public class ProductDetailDto
{
    public int FKProductId { get; set; }
    public string Name { get; set; }
}

public class ProductModels
{
    public class ProductCategoryListModel
    {
        public IEnumerable<ProductDetailDto> ProductDetails { get; set; }
        public IEnumerable<SelectListItem> ProductCategoryList { get; set; }
    }
}

#endregion

#region Services

public interface IProductService
    {
        IEnumerable<ProductDetailDto> GetAllEffectiveProductDetails()
    }

public class ProductService : IProductService
{
    public IEnumerable<ProductDetailDto> GetAllEffectiveProductDetails()
    {
        throw new NotImplementedException();
    }
}

#endregion

然后您轻松创建IProductService 的模拟实例,将其传递给YourController 的构造函数,设置GetAllEffectiveProductDetails 方法并检查返回的ActionResult 及其model。

[TestClass]
public class YourControllerTest
{
    private Mock<IProductService> productServiceMock;

    private YourController target;

    [TestInitialize]
    public void Init()
    {
        productServiceMock = new Mock<IProductService>();

        target = new YourController(
            productServiceMock.Object);
    }

    [TestMethod]
    public void Products()
    {
        //arrange
        // There is a setup of 'GetAllEffectiveProductDetails'
        // When 'GetAllEffectiveProductDetails' method is invoked 'expectedallProducts' collection is exposed.
        var expectedallProducts = new List<ProductDetailDto> { new ProductDetailDto() };
        productServiceMock
            .Setup(it => it.GetAllEffectiveProductDetails())
            .Returns(expectedallProducts);

        //act
        var result = target.Products();

        //assert
        var model = (result as ViewResult).Model as ProductModels.ProductCategoryListModel;
        Assert.AreEqual(model.ProductDetails, expectedallProducts);
        /* Any other assertions */
    }
}

【讨论】:

  • 我也会这样做。 +1
  • 但是在生产环境中,MVC框架会自动调用控制器的构造函数并带有IProduceService的实例吗?
  • 为了让MVC自动调用带有IProductService实例的控制器的构造函数需要实现自定义控制器工厂,否则你会得到"No parameterless constructor defined for this object" exception。下面的链接包含一个实现示例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-12
  • 1970-01-01
  • 2011-12-24
  • 1970-01-01
  • 2012-06-01
  • 1970-01-01
相关资源
最近更新 更多