Первый модульный тест (VS2010 C #) - PullRequest
3 голосов
/ 15 февраля 2011

Это мое первое знакомство с модульным тестированием, и я пытаюсь понять, как можно использовать эту концепцию при простой проверке даты.

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

    private void CheckToDate(DateTime ToDate)
    {
        if (Manager.MaxToDate < ToDate.Year)
            //show a custom message
    }

Как можно использовать модульные тесты в этом случае?

С уважением,

Alex

Спасибо за ваши ответы:

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

public bool IsDateValid(DateTime toDate)
{
    return (Manager.MaxToDate < toDate.Year);
}

Ответы [ 4 ]

5 голосов
/ 15 февраля 2011

Да, это возможно.Но юнит-тестирование меняет дизайн вашего класса.Чтобы сделать возможным модульное тестирование этого кода, вы должны внести следующие изменения:

  1. Сделайте ваш метод общедоступным.(Можно сделать его защищенным, но для простоты сделайте его общедоступным.)

  2. Извлеките все внешние зависимости этого метода для интерфейса, чтобы вы могли их высмеивать.Затем вы можете использовать некоторую библиотеку-макет ( moq , Rhino.Mocks ) для имитации реальных зависимостей и записи утверждений.

  3. Запись теста.

Вот пример кода.

Тестируемый класс:

public class ClassUnderTest
{
    public IManager Manager {get;set;}
    public IMessanger Messanger {get;set}

    public  ClassUnderTest (IManager manager, IMessanger messanger)
    {
        Manager = manager;
        Messanger = messanger;
    }

    private void CheckToDate(DateTime ToDate)
    {
        if (Manager.MaxToDate < ToDate.Year)
            //show a custom message
            Messanger.ShowMessage('message');
    }
}

Тест:

[TestFixture]
public class Tester
{
    public void MessageIsShownWhenDateIsLowerThanMaxDate()
    {
        //SetUp
        var manager = new Mock<IManager>();
        var messanger = new Mock<IMessanger>();

        var maxDate = DateTime.Now;

        manager.Setup(m => m.MaxToDate).Returns(maxDate);

        var cut = new ClassUnderTest (manager.Object, messanger.Object);

        //Act
        cut.CheckToDate();

        //Assert
        messanger.Verify(foo => foo.ShowMessage("message"), Times.AtLeastOnce())
    }
}

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

2 голосов
/ 15 февраля 2011

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

Что касается "Можно ли создать модульные тесты для чего-то подобного?", это зависит от того, насколько чистым вы хотите быть по концепции юнит-тестов, насколько зависимым от пользователя вы хотите, чтобы они были, и что именно делает //show a custom message.

Насколько чистыми вы хотите, чтобы ваши юнит-тестыбыть?Если вам все равно, являются ли они грязными хакерами, то вы можете использовать отражения, чтобы предоставить приватный метод для своих модульных тестов, и просто вызвать его напрямую.Это, в общем-то, плохая практика, потому что ваши частные функции по определению могут быть изменены.В противном случае вы просто сделаете их общедоступными.

Если //show a custom message выводит на консоль, то вы можете довольно легко выполнять тесты, выполняемые без вывода сообщений.Если вы действительно хотите проверить вывод, вам нужно подключиться к вашему Console.Out, чтобы вы могли увидеть, что было напечатано, и добавить соответствующие утверждения.

Если //show a custom message использует MessageBox.Show, тогдавам, возможно, придется сделать автоматизированный тест пользовательского интерфейса, чтобы иметь возможность проверить это.Ваши тесты не будут работать в фоновом режиме и будут прерываться, если вы будете двигать мышью во время выполнения теста.

Если вы не хотите выполнять автоматический тест пользовательского интерфейса просто для тестированиялогика этого класса, лучший из известных мне способов, - это модифицировать ваш класс, чтобы использовать внедрение зависимостей.Инкапсулируйте весь фактический выходной код (MessageBox.Show) в другой класс, абстрагируйте его с помощью интерфейса или абстрактного базового класса и сделайте так, чтобы ваш исходный класс ссылался на абстрактный тип.Таким образом, вы можете внедрить макет в свои тесты, и он не будет выводиться на экран.

public interface INotification
{
    void ShowMessage(string message);
}

public class MessageBoxNotification : INotification
{
    public void ShowMessage(string message)
    {
        MessageBox.Show(message);
    }
}

public class MyClass
{
    private INotification notification;

    public MyClass(INotification notification)
    {
        this.notification = notification;
    }

    public void SomeFunction(int someValue)
    {
        // Replace with whatever your actual code is...
        ToDate toDate = new SomeOtherClass().SomeOtherFunction(someValue);
        CheckToDate(toDate);
    }

    private void CheckToDate(DateTime ToDate)
    {
        if (Manager.MaxToDate < ToDate.Year)
            notification.Show("toDate, too late!: " + toDate.ToString());
    }
}

Ваш модульный тест создаст свой собственный класс INotification, передав его конструкторуMyClass и вызовите метод SomeFunction.

Возможно, вы захотите абстрагировать такие вещи, как Manager, и классы будут включать в вычисления ToDate аналогичным образом.

2 голосов
/ 15 февраля 2011

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

Вы не можете обнаружить блоки сообщений в модульных тестах, а также не хотите запускать всю среду графического интерфейса из своих модульных тестов. Самый простой способ обойти это - скрыть фактический код, отображающий окно сообщения, в отдельном методе, в идеале в отдельном интерфейсе. Затем вы можете внедрить фиктивную реализацию этого интерфейса для своих модульных тестов. Этот макет ничего не отображает, просто записывает переданное ему сообщение, чтобы вы могли проверить его в своем модульном тесте.

Другая проблема заключается в том, что ваш метод private. Сначала проверьте, откуда он вызывается, и может ли он быть вызван с помощью открытого метода без особых сложностей. Если нет, вам может потребоваться сделать его (временно) общедоступным, чтобы включить модульное тестирование. Обратите внимание, что необходимость в модульном тестировании частных методов обычно является запахом проекта: ваш класс может пытаться сделать слишком много, принимая на себя слишком много различных обязанностей. Возможно, вы сможете извлечь некоторые из его функций в отдельный класс, где он станет общедоступным, что позволит напрямую тестировать его. Но сначала вам нужно пройти эти юнит-тесты, чтобы убедиться, что вы ничего не сломали при рефакторинге.

Затем вам нужно настроить Manager.MaxToDate до теста с подходящей датой и вызвать CheckToDate с различными параметрами, чтобы убедиться, что результат соответствует ожидаемому.

Рекомендуемое значение для подобных уловок и других: Эффективная работа с устаревшим кодом .

1 голос
/ 15 февраля 2011

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

  • Он проверяет дату
  • Он реагирует на неудачную проверку, показывая сообщение

Хорошо разработанный метод делает только одно.Итак, я бы рекомендовал немного реорганизовать код, чтобы вы получили метод валидации, который ничего не делает, кроме валидации.Этот метод будет очень прост для тестирования:

public bool IsDateValid(DateTime toDate)
{
    // just guessing on the rules here...
    return (Manager.MaxToDate >= toDate.Year);
}

Это также сделает код проверки более пригодным для повторного использования, поскольку он переносит решение о том, как обрабатывать результат в вызывающем коде.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...