简体   繁体   中英

Mocking static method options

I understand that you can't mock a static method with moq, but I was wondering what my possible options are

I have a controller class defined

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));
    }
}

And my unit test looks like this:

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);
}

The problem is in the private method where it calls SelectLists.ContactClassifications(false), it then tries to hit the database.

The SelectList class is defined like

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

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

and it is the line where it calls GetContactClassifications in the SelectList that it feels like I should be able to mock (if the method that calls it can't be mocked because it is static). This one does implement an interface.

Even if there is some way that the private method in the Controller (InitialiseContactVM) could be mocked it would suit me.

Is there any way to achieve any of these things?

应该重构SelectLists类,以允许您注入IReferenceDataManager而不是实例化一个。

Ideally, your DAL should not be made out of static methods, but normal objects that provide services injected though an interface into controllers or whatever needs it.

But if you can't/don't want to change it, you the "standard" way to let you mock it would be to decouple the static method call from your controller. It can be done by wrapping it in a class that contains the static call and implements an interface, that is injected in the controller, and therefore mocked out in the tests. It's somewhat similar to testing a MessageBox call or the current system date/time.

First create an interface that will contain your static method calls:

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

Then a class will implement it by calling the actual static method:

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

In the controller, you take an instance of this class in the constructor, save it to a local variable, and use that to call the static method though the wrapper:

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));
}

Finally, in the test, you just pass a mock of the wrapper and setup it to return whatever makes sense to that test.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM