2011-09-02 2 views
5

Это мой первый вопрос, поэтому, пожалуйста, будьте добрыми! :)Как использовать Moq для проверки метода без возвращаемого значения?

Что я пытаюсь сделать, это написать несколько тестов для класса менеджера, который во время построения добавляет много новых экземпляров одного класса элемента в список. Когда в этом классе диспетчера вызывается UpdateAllItems, предполагается, что он переименовывает список и вызывает Increment для каждого отдельного элемента.

Класс менеджера - это мой код, но единственный класс элемента не так, я не могу его изменить.

Я использую NUnit для тестирования и начинаю работать с Moq. Поскольку класс менеджера использует единственный класс элемента, я бы подумал, что мне нужно использовать Moq, поэтому я тестирую только менеджера, а не отдельный элемент.

Как написать тесты для моего метода UpdateAllItems? (Технически я должен сначала написать тесты, которые я знаю).

Вот пример кода, который дает общее представление о том, что я работаю с ...

public class SingleItem_CodeCantBeModified 
{ 
    public int CurrentValue { get; private set; } 

    public SingleItem_CodeCantBeModified(int startValue) 
    { 
     CurrentValue = startValue; 
    } 

    public void Increment() 
    { 
     CurrentValue++; 
    } 
} 

public class SingleItemManager 
{ 
    List<SingleItem_CodeCantBeModified> items = new List<SingleItem_CodeCantBeModified>(); 

    public SingleItemManager() 
    { 
     items.Add(new SingleItem_CodeCantBeModified(100)); 
     items.Add(new SingleItem_CodeCantBeModified(200)); 
    } 

    public void UpdateAllItems() 
    { 
     items.ForEach(item => item.Increment()); 
    } 
} 

Заранее спасибо за помощь!

+0

Как этот класс полезен? Он создает частный список счетчиков и обновляет каждый элемент каждый раз, когда вызывается метод. Должно быть какое-то изменение в поведении объекта. если UpdateAllItems не работает - что бы наблюдалось изменение? – Gishu

ответ

5

Простой ответ: вы не можете. Метод, который вызывает UpdateAllItems (Increment()), не является виртуальным, поэтому вы не сможете его издеваться.

Ваши варианты, как я вижу, являются:

  • Не испытывай UpdateAllItems вообще. Его реализация тривиальна, поэтому это вариант рассмотрения (хотя и не идеальный).
  • Создайте реальные SingleItem_CodeCantBeModified экземпляров в вашем тесте. Пуристы сказали бы, что у вас больше нет теста , но он все равно может оказаться полезным.
  • Добавить интерфейс ISingleItem и класс SingleItemAdapter : ISingleItem, который содержит ссылку на SingleItem_CodeCantBeModified и переадресовывает вызовы. Затем вы можете написать SingleItemManager для работы на ISingleItem с, и вы сможете бесплатно пропустить в своих тестах ISingleItem s. (В зависимости от того, как ваша система настроена, вы могли бы даже быть в состоянии спуститься с SingleItem_CodeCantBeModified, реализовать интерфейс на вашем потомка, и использовать эти объекты вместо того, чтобы писать адаптер.)

Этот последний вариант дает вам большинство вариантов, но ценой некоторой сложности. Выберите вариант, который лучше всего подходит для того, что вы пытаетесь выполнить.

1

Ваш менеджер слишком зависим от предмета (в List<Item>). Можете ли вы извлечь список в отдельный класс, чтобы его можно было издеваться? например:

public SingleItemManager() 
{ 
    items.Add(ItemRepository.Get(100)); 
    items.Add(ItemRepository.Get(200)); 
} 

Тестирование (часть кода опущены):

int i = 0; 

var itemMock = new Mock<Item>(); 
itemMock.Setup(i => i.Increment()).Callback(() => i++); 

var repositoryMock = new Moc<ItemRepository>(); 
repositoryMock.Setup(r => r.Get(It.IsAny<int>()).Returns(itemMock.Object); 

var manager = new SingleItemManager(); 
manager.UpdateAllItems(); 

Assert.AreEqual(i, 1); 
-1

Вместо жесткого кодирования ваши дополнительные конкретные элементы, имеют SingleItem_CodeCantBeModified реализовать интерфейс (или встроить его в обертку, которая реализует интерфейс), затем перейдите на (новую) фабрику, которая создаст эти элементы.

В своем тесте вы создадите макет фабрики, чтобы перейти в ваш класс Manager, затем вы можете контролировать, какие методы вызывают на этом издеваемом объекте.

Хотя это было бы больше о тестировании внутренних компонентов системы, а не побочных продуктов. Какой интерфейс реализует менеджер? Если это не доказывает себя внешне, какие результаты вы испытываете?

+0

SingleItem_ ** CodeCantBeModified ** – riezebosch

+0

@riezebosch: Если нажатие наступает, вы можете создать прокси-объект, который реализует межфактный интерфейс и просто проходит через вызовы методов. Это не то же самое, что gettting класс для реализации интерфейса, но имеет тот же эффект. – Chris

0

Как обычно, вы можете добавить еще один уровень косвенности.

  1. Создать класс-обертку SingleItem_CodeCantBeModified
  2. Сделать это обертка наследуют IItem интерфейс
  3. Сделать SingleItemManager зависят от IItem вместо SingleItem_CodeCantBeModified

ИЛИ

Если Increment виртуальный метод (Я понимаю, что это не в вашем примере кода, но на всякий случай), использование partial mocking.

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