【问题标题】:Mocking static method options模拟静态方法选项
【发布时间】:2013-08-19 02:46:15
【问题描述】:

我知道您不能使用 moq 模拟静态方法,但我想知道我可能的选择是什么

我定义了一个控制器类

public class CustomerController : BaseController
{
    private ICustomerManager cm;

    public CustomerController()
        : this(new CustomerManager())
    {
    }

    public CustomerController(ICustomerManager customerMan)
{
    cm = customerMan;
}

    public ActionResult EditContact(ContactVM model,  IEnumerable<HttpPostedFileBase> Files, PageAction pageAction)
    {
        if (ModelState.IsValid)
        {
            InitializeContactVM(model); //throws an error
        }
    }

    private void InitializeContactVM(ContactVM model)
    {
        model.Customer = cm.GetViewFindCustomerDetails((int)model.CustomerId);
        model.ContactClassificationList = AddBlankToList(SelectLists.ContactClassifications(false)); 
        model.ContactSourceList = AddBlankToList(SelectLists.ContactSources(false));
    }
}

我的单元测试看起来像这样:

public void Edit_Contact_Update_Existing_Contact()
{
    var dataManager = new Mock<IReferenceDataManager>();
    //dataManager.Setup(a=>a.GetContactClassifications()).Returns()
    var contact = InitializeContact();
    var contactvm = new ContactVM(contact);
    var fileMock = new Mock<HttpPostedFileBase>();
    var files = new[] {fileMock.Object};

    var mocManager = InitializeMocManagerContact();
    mocManager.Setup(a => a.GetContactById(It.IsAny<int>())).Returns(contact);
    mocManager.Setup(a => a.UpdateContact(It.IsAny<ContactVM>(), It.IsAny<string>())).Returns(contact);

    var controller = new CustomerController(mocManager.Object);
    var controllerContext = InitializeContext();
    controller.ControllerContext = controllerContext.Object;
    //  mocManager.CallBase = true;

    var result = controller.EditContact(contactvm, files, PageAction.Default) as ViewResult;
    var model = result.ViewData.Model as ContactVM;

    Assert.IsTrue(model.ContactId == contact.CONTACT_ID);
}

问题出在调用 SelectLists.ContactClassifications(false) 的私有方法中,然后它尝试访问数据库。

SelectList 类的定义如下

public static class SelectLists
{
    private static readonly ReferenceDataManager _dataManager = new ReferenceDataManager();

    public static SelectList ContactClassifications(bool includeDeleted)
    {
        var data = _dataManager.GetContactClassifications();
    }
}

它是它在 SelectList 中调用 GetContactClassifications 的那一行,感觉我应该能够模拟(如果调用它的方法因为它是静态的而不能被模拟)。这个确实实现了一个接口。

即使有某种方式可以模拟控制器中的私有方法 (InitialiseContactVM),它也适合我。

有没有办法实现这些目标?

【问题讨论】:

    标签: c# asp.net-mvc unit-testing moq


    【解决方案1】:

    理想情况下,您的 DAL 不应该由静态方法组成,而是由提供通过接口注入控制器或任何需要它的任何服务的普通对象组成。

    但是,如果您不能/不想更改它,那么让您模拟它的“标准”方法是将静态方法调用与控制器分离。它可以通过将其包装在包含静态调用并实现接口的类中来完成,该接口被注入控制器中,因此在测试中被模拟出来。这有点类似于测试MessageBox 调用或当前系统日期/时间。

    首先创建一个包含静态方法调用的接口:

    public interface ISelectListsWrapper
    {
        SelectList ContactClassifications(bool includeDeleted);
    }
    

    然后一个类会通过调用实际的静态方法来实现它:

    public class SelectListsWrapper : ISelectListsWrapper
    {
        public SelectList ContactClassifications(bool includeDeleted)
        {
            return SelectLists.ContactClassifications(includeDeleted);
        }
    }
    

    在控制器中,你在构造函数中获取这个类的一个实例,将它保存到一个局部变量中,并使用它通过包装器调用静态方法:

    private readonly ISelectListsWrapper selectLists;
    
    public CustomerController(ICustomerManager customerMan, ISelectListsWrapper selectLists)
    {
        cm = customerMan;
        this.selectLists = selectLists;
    }
    
    private void InitializeContactVM(ContactVM model)
    {
        model.Customer = cm.GetViewFindCustomerDetails((int)model.CustomerId);
        model.ContactClassificationList = AddBlankToList(this.selectLists.ContactClassifications(false)); 
        model.ContactSourceList = AddBlankToList(this.selectLists.ContactSources(false));
    }
    

    最后,在测试中,您只需传递包装器的模拟并设置它以返回对该测试有意义的任何内容。

    【讨论】:

    • 干杯亚历杭德罗,非常感谢您的详细回复,我已经进行了修改,对我来说效果很好
    【解决方案2】:

    SelectLists 类应该被重构以允许你注入一个IReferenceDataManager 而不是自己实例化一个。

    【讨论】:

    • 我会尝试这样做,看看我是否有任何进展
    • 如果我是你,我会认真考虑让SelectList 不是静态类,而是在控制器中拥有它的实例。你应该看看使用依赖注入。 ASP.NET MVC 4 对此有很好的支持。 asp.net/mvc/tutorials/hands-on-labs/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多