2013-08-19 3 views
0

Я понимаю, что вы не можете издеваться статическим методом с MOQ, но мне было интересно, что мои возможные вариантыMocking статических вариантов метода

У меня есть класс контроллера определен

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 (ложь), он пытается ударить базу данных.

Класс SelectList определяется как

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

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

и это линия, где она называет GetContactClassifications в SelectList, что он чувствует, что я должен быть в состоянии насмехаться (если метод, который вызывает это не может быть издевается, потому что он статичен). Это реализует интерфейс.

Даже если есть способ, которым можно было бы издеваться над личным методом в контроллере (InitialiseContactVM), это подойдет мне.

Есть ли способ достичь любой из этих вещей?

ответ

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

Наконец, в тесте вы просто передаете макет обертки и настройте его, чтобы вернуть все, что имеет смысл для этого теста.

+0

Приветствия Алехандро, спасибо за подробный ответ, я внес изменения, и он работает хорошо для меня – jazza1000

1

Класс SelectLists должен быть реорганизован, чтобы вы могли вводить IReferenceDataManager, а не создавать его сами.

+0

Я попробую сделать это и посмотреть, получу ли я дальше – jazza1000

+0

Если бы я был вами, я бы серьезно подумал о том, что 'SelectList' не является статическим классом, а имеет экземпляр его в контроллере. Вы должны посмотреть на использование инъекции зависимостей. ASP.NET MVC 4 имеет хорошую поддержку для этого. http://www.asp.net/mvc/tutorials/hands-on-labs/aspnet-mvc-4-dependency-injection –