Модульное тестирование ASP.NET MVC с объектом MOQ

Как лучше всего издеваться над приведенным ниже кодом в модульном тестировании:

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.

Может ли кто-нибудь помочь мне в этом, что является лучшим методом тестирования с макетным объектом для вышеуказанного метода?


person Pawan    schedule 12.11.2013    source источник
comment
Я предполагаю, что вы, вероятно, захотите создать Moq System.Web.HttpContextBase (настройка Moq User, Request, Response, Session, Cache, Server и т. Д. По мере необходимости).   -  person James S    schedule 12.11.2013
comment
да, я хочу создать объект MOQ.   -  person Pawan    schedule 12.11.2013
comment
Нужна дополнительная информация. Вы пытаетесь написать модульный тест для вышеуказанного метода? И вы пытаетесь имитировать зависимости, такие как ProductService? Вы упомянули, как лучше всего издеваться над кодом ниже в модульном тестировании. Какой из приведенных ниже кодов вы именно здесь хотите поиздеваться?   -  person Spock    schedule 12.11.2013
comment
›› Да, я пытаюсь написать модульный тест с помощью moq. ›› И я хочу знать, как я могу имитировать зависимости, такие как ProductService, в моем примере?   -  person Pawan    schedule 12.11.2013


Ответы (1)


Если вы хотите сначала издеваться над ProductService, вам нужно внедрить эту зависимость.

Внедрение конструктора - это наиболее распространенный подход для контроллеров в 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 */
    }
}
person Ilya Palkin    schedule 17.11.2013
comment
Я бы тоже так поступил. +1 - person hutchonoid; 18.11.2013
comment
Но будет ли в производственной среде MVC-платформа автоматически вызывать конструктор контроллера с экземпляром IProduceService? - person Yngvar Kristiansen; 27.06.2014
comment
Чтобы сделать конструктор контроллера вызовов MVC с экземпляром IProductService автоматически , необходимо реализовать настраиваемую фабрику контроллеров, иначе вы получите Для этого исключения объекта не определен конструктор без параметров. Ссылка ниже содержит пример реализации. - person Ilya Palkin; 28.06.2014