2016-01-28 3 views
4

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

Я блок тестирования MVC контроллер ASP.Net:

public class TransactionController : Controller 
{ 
    private readonly ITransactionService _transactionService; 
    private readonly SearchPanelVmBuilder _searchPanelVmBuilder; 
    private readonly TransactionVmsBuilder _transactionVmsBuilder; 

    public TransactionController(TransactionVmsBuilder transactionVmsBuilder, ITransactionService transactionService, SearchPanelVmBuilder searchPanelVmBuilder) 
    { 
     _transactionVmsBuilder = transactionVmsBuilder; 
     _transactionService = transactionService; 
     _searchPanelVmBuilder = searchPanelVmBuilder; 
    } 

    // other methods omitted for brevity 

    public PartialViewResult SearchPanel() 
    { 
     var vm = _searchPanelVmBuilder.BuildVm(); 

     return PartialView("_SearchPanel", vm); 
    } 
} 

Блок тестовый код:

[Fact] 
public void SeachPanel_Calls_BuildSearchPanelVm() 
{ 
    // Arrange 
    var mockTransService = new Mock<ITransactionService>(); 
    var mockTransVmsBuilder = new Mock<TransactionVmsBuilder>(); 
    var mockSearchPanelVmBuilder = new Mock<SearchPanelVmBuilder>(); 
    var controller = new TransactionController(mockTransVmsBuilder.Object, mockTransService.Object, mockSearchPanelVmBuilder.Object); 

    // Act 
    controller.SearchPanel(); 

    // Assert 
    mockSearchPanelVmBuilder.Verify(x => x.BuildVm()); 
} 

MOQ жалуется:

Невозможно создать прокси класса: MCIP.Web.UI.ViewModelBuilders.Singular.SearchPanelVmBuilder. Не удалось найти конструктор без параметров.

Класс не может создать экземпляр прокси для:

public class SearchPanelVmBuilder 
{ 
    private readonly ITransactionTypeService _transactionTypeService; 
    private readonly TransactionTypeVmBuilder _transactionTypeVmBuilder; 
    private readonly UserProvider _userProvider; 

    public SearchPanelVmBuilder(
     UserProvider userProvider, 
     ITransactionTypeService transactionTypeService, 
     TransactionTypeVmBuilder transactionTypeVmBuilder 
     ) 
    { 
     _userProvider = userProvider; 
     _transactionTypeService = transactionTypeService; 
     _transactionTypeVmBuilder = transactionTypeVmBuilder; 
    } 

    public virtual SearchPanelVm BuildVm() 
    { 
     return new SearchPanelVm 
     { 
      Userlist = _userProvider.GetOperators(), 
      TransactionTypes = 
       _transactionTypeService.GetAll().Select(x => _transactionTypeVmBuilder.BuildVmFromModel(x)).ToList() 
     }; 
    } 
} 

его соответствующих зависимостей:

public class UserProvider 
{ 
    private static int retryCount; 

    public virtual List<string> GetOperators()... 

    public virtual List<string> GetGroupsForUser(WindowsIdentity identity)... 
} 

public interface ITransactionTypeService 
{ 
    List<TransactionType> GetAll(); 
} 

public class TransactionTypeVmBuilder 
{ 
    public virtual TransactionTypeVm BuildVmFromModel(TransactionType transactionType)... 
} 

я делаю что-то не так?

Должен ли я явно указывать MOQ, чтобы идти в авто-насмешливых вложенных зависимостях?

Или я должен явно настроить вложенную Mocks - как это:

var mockUserProvider = new Mock<UserProvider>(); 
var mockTransTypeService = new Mock<ITransactionTypeService>(); 
var mockTransactionTypeVmBuilder = new Mock<TransactionTypeVmBuilder>(); 
var mockSearchPanelVmBuilder = new Mock<SearchPanelVmBuilder>(mockUserProvider.Object, mockTransTypeService.Object, mockTransactionTypeVmBuilder.Object); 
+0

Лично я стараюсь, чтобы мои классы зависели от интерфейсов, а не от конкретных классов, поэтому я могу высмеять свое сердце во время модульного тестирования и действительно ограничить объем каждого теста. – ESG

+0

Короче говоря, наличие интерфейсов не решит проблему. Классы, у которых нет интерфейсов, имеют виртуальные методы - их все равно можно издеваться так же, как интерфейс.Поскольку для них никогда не будет альтернативной реализации, интерфейсы, которые просто позволяют насмехаться, не нужны. – JTech

ответ

1

Да, ваше предположение верно. Поскольку класс SearchPanelVmBuilder не предоставили конструктор без параметров Mock для этого класса не может быть создан так:

var mockSearchPanelVmBuilder = new Mock<SearchPanelVmBuilder>()

Вызывает исключение: Could not find a parameterless constructor.


Вместо создания Mock, предоставляя все параметры, что-то вроде этого:

UserProvider userProvider = new UserProvider(); 
Mock<ITransactionTypeService> transactionTypeService = new Mock<ITransactionTypeService>(); 
TransactionTypeVmBuilder transactionTypeVmBuilder = new TransactionTypeVmBuilder(); 

// Use constructor with parameters here because SearchPanelVmBuilder 
// doesn't have parameterless constructor 
var mockSearchPanelVmBuilder = new Mock<SearchPanelVmBuilder>(
    userProvider, transactionTypeService.Object, transactionTypeVmBuilder); 

var mockTransService = new Mock<ITransactionService>(); 
var mockTransVmsBuilder = new Mock<TransactionVmsBuilder>(); 

var controller = new TransactionController(
    mockTransVmsBuilder.Object, 
    mockTransService.Object, 
    mockSearchPanelVmBuilder.Object); 

Затем экземпляр контроллера TransactionController может быть создан и метод SearchPanel можно назвать его.

+0

Спасибо за ответ! Таким образом, для Moq нет возможности автоматически разрешать вложенные зависимости, например StructureMap? – JTech

+0

Добро пожаловать! Насколько я понимаю, как работают конструкторы в C#, их нет. Но проверьте, например. [этот ответ] (http://stackoverflow.com/questions/5262881/mocking-objects-without-no-argument-constructor-in-c-sharp-net). Или [это] (http://stackoverflow.com/questions/25649155/can-not-instantiate-proxy-could-not-find-a-parameterless-constructor). – dee

1

В этом случае, все, что вы хотите проверить, что BuildVM называется и результат возвращается, так что вы не нужно издеваться над внутренними зависимостями. Вам необходимо настроить возвращаемое значение для метода BuildVM в разделе «Упорядочить», прежде чем вы вызовете метод SearchPanel.

mockSearchPanelVmBuilder.Setup(x => x.BuildVM()).Returns(mockSearchPanelVM); 

Поскольку вы издеваетесь над классом и виртуальным методом, если вы не настроите возвращаемое значение, будет выполняться фактическая реализация. С интерфейсом будет выведена ошибка, указывающая, что вы должны установить возвращаемое значение.

Смежные вопросы