Как использовать Moq для проверки метода без возвращаемого значения? - PullRequest
5 голосов
/ 02 сентября 2011

Это мой первый вопрос, поэтому будьте добры! :)

То, что я пытаюсь сделать, - это написать несколько тестов для класса менеджера, который во время построения добавляет много новых экземпляров одного класса элементов в список. Когда 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());
    }
}

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

Ответы [ 4 ]

5 голосов
/ 02 сентября 2011

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

Ваши варианты, на мой взгляд, таковы:

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

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

1 голос
/ 02 сентября 2011

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

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);
0 голосов
/ 02 сентября 2011

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

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

OR

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

0 голосов
/ 02 сентября 2011

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

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

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

...