簡體   English   中英

模擬靜態方法選項

[英]Mocking static method options

我知道你不能用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的行,它感覺我應該能夠模擬(如果調用它的方法不能被模擬,因為它是靜態的)。 這個實現了一個接口。

即使有一些方法可以模擬Controller(InitialiseContactVM)中的私有方法,它也適合我。

有沒有辦法實現這些東西?

應該重構SelectLists類,以允許您注入IReferenceDataManager而不是實例化一個。

理想情況下,您的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));
}

最后,在測試中,您只需傳遞包裝器的模擬並設置它以返回對該測試有意義的任何內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM