Пошаговое руководство. Создание и запуск модульных тестов для управляемого кода

Экспорт (0) Печать
Свернуть все
MSDN Library
Средства разработки
Mobile and Embedded Development
Разработка на .NET
Разработка решений Office
Разработка для OneDrive
patterns & practices
Microsoft Azure
Серверы и средства разработки на уровне предприятия
Веб-разработки
Технические статьи

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

# Пошаговое руководство. Создание и запуск модульных тестов для управляемого кода

Другие версии

- Visual Studio 2010
- Visual Studio 2012

34 из 36 оценили этот материал как полезный - Оценить эту тему

В этом пошаговом руководстве приводится подробное описание процесса создания, запуска и настройки набора модульных тестов с помощью платформы модульных тестов Microsoft для управляемого кода и обозревателя тестов Visual Studio. В руководстве производится создание проекта C#, находящегося в стадии разработки, создание тестов для проверки его кода, запуск тестов и изучение результатов. После этого производится изменение кода проекта и повторный запуск тестов.

Этот раздел содержит следующие подразделы.

Подготовка к выполнению пошагового руководства

Создание проекта модульного теста

Создание тестового класса

- Требования к тестовому классу

Создание первого тестового метода

- Требования к методу теста

Сборка и запуск теста

Исправление кода и повторный запуск тестов

Использование модульных тестов для улучшения кода

Примечание
В этом пошаговом руководстве используется платформа модульных тестов Microsoft для управляемого кода. Обозреватель тестов также может запускать тесты c платформ модульных тестов стороннего производителя, которые имеют адаптеры для обозревателя тестов. Дополнительные сведения см. в разделе Практическое руководство. Установка платформ модульного тестирования сторонних поставщиков.

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

##
Обязательные компоненты

- Проект "Банк". См. раздел Пример проекта для создания модульных тестов.

##
Подготовка к выполнению пошагового руководства

### Подготовка к выполнению пошагового руководства

1. Запустите Visual Studio.

2. В меню Файл выберите пункт Создать, а затем команду Проект.

   Откроется диалоговое окно Создание проекта.

3. В области Установленные шаблоны выберите шаблон Visual C#.

4. В списке типов приложения выберите пункт Библиотека классов.

5. В поле Имя введите Bank и нажмите кнопку ОК.

   Примечание
   Если имя "Bank" уже существует, выберите другое имя для проекта.

   Будет создан новый проект Bank. Этот проект отобразится в обозревателе решений, а его файл Class1.cs откроется в редакторе кода.

   Примечание
   Если файл Class1.cs не откроется в редакторе кода, дважды щелкните Class1.cs в обозревателе решений, чтобы открыть этот файл.

6. Скопируйте исходный код из раздела Пример проекта для создания модульных тестов.

7. Замените исходное содержимое файла Class1.cs кодом из примера Пример проекта для создания модульных тестов.

8. Сохранение файла как BankAccount.cs

9. В меню Построение выберите Построить решение.

Будет создан проект с именем "Bank". Он содержит исходный код, подлежащий тестированию, и средства для его тестирования. Пространство имен проекта "Bank", BankAccountNS, содержит открытый класс BankAccount, методы которого будут тестироваться в приведенных ниже процедурах.

В данном кратком руководстве рассматривается метод Debit. Метод Debit вызывается после снятия денег со счета и содержит следующий код:

C#
Копировать

    // method under test
    public void Debit(double amount)
    {
        if(amount > m_balance)
        {
            throw new ArgumentOutOfRangeException("amount");
        }
        if (amount < 0)
        {
            throw new ArgumentOutOfRangeException("amount");
        }
        m_balance += amount;
    }

##
Создание проекта модульного теста

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

### Создание проекта модульного теста

1. В меню Файл последовательно щелкните Добавить и Новый проект....

2. В диалоговом окне "Новый проект" разверните узлы Установленные и Visual C# и выберите Тест.

3. В списке шаблонов выберите Проект модульного теста.

4. В поле Имя введите BankTest и нажмите кнопку ОК.

   Проект BankTests добавляется в решение Банк.

5. В проекте BankTests добавьте ссылку на решение Банк.

   В области "Обозреватель решений" выберите Ссылки в проекте BankTests, а затем выберите Добавить ссылку... из контекстного меню.

6. В диалоговом окне "Диспетчер ссылок" разверните Решение и выбирите элемент Bank.

##
Создание тестового класса

Тестовый класс нужен для проверки класса BankAccount. Можно использовать UnitTest1.cs, созданный в шаблоне проекта, но лучше дать файлу и классу более описательные имена. Можно сделать это за один шаг, переименовав файл в обозревателе решений.

Переименование файла класса

В обозревателе решений выберите файл UnitTest1.cs в проекте BankTests. В контекстном меню выберите команду Переименовать, а затем переименуйте файл в BankAccountTests.cs. Выберите Да в диалоговом окне, предлагающем переименовать все ссылки на элемент кода "UnitTest1" в проекте. Этот шаг изменяет имя класса на BankAccountTest.

Файл BankAccountTests.cs теперь содержит следующий код:

C#
Копировать

    // unit test code
    using System;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    namespace BankTests
    {
        [TestClass]
        public class BankAccountTests
        {
            [TestMethod]
            public void TestMethod1()
            {
            }
        }
    }

Добавление оператора using в тестируемый проект

Можно также добавить оператор using в класс, чтобы тестируемый проект можно было вызывать без использования полных имен. Вверху файла класса добавьте:

C#
Копировать

    using BankAccountNS

### Требования к тестовому классу

Минимальные требования к тестовому классу следующие:

- Атрибут [TestClass] является обязательным для платформы модульных тестов Microsoft для управляемого кода в любом классе, содержащем методы модульных тестов, которые необходимо выполнить в обозревателе тестов.

- Каждый метод теста, который требуется выполнять с помощью обозревателя тестов, должен иметь атрибут [TestMethod].

Кроме того, в проекте модульного теста могут быть классы без атрибута [TestClass], а в тестовых классах могут быть методы без атрибута [TestMethod]. Можно использовать эти другие классы и методы в методах теста.

##
Создание первого тестового метода

В этой процедуре мы напишем методы модульного теста для проверки поведения метода Debit класса BankAccount. Этот метод приведен выше.

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

1. Метод создает исключение [ArgumentOutOfRangeException], если сумма кредита превышает баланс.

2. Он также создает ArgumentOutOfRangeException, если размер кредита меньше нуля.

3. Если проверка пунктов 1.) и 2.) проходит успешно, то метод вычитает сумму из баланса счета.

В первом тесте проверим, что при допустимом размере кредита (со значением, меньшим баланса счета и большим нуля) со счета снимается нужная сумма.

### Создание метода теста

1. Добавьте инструкцию using BankAccountNS; в файл BankAccountTests.cs.

2. Добавьте следующий метод в этот класс BankAccountTests:

   C#
   Копировать

       // unit test code
       [TestMethod]
       public void Debit_WithValidAmount_UpdatesBalance()
       {
           // arrange
           double beginningBalance = 11.99;
           double debitAmount = 4.55;
           double expected = 7.44;
           BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

           // act
           account.Debit(debitAmount);

           // assert
           double actual = account.Balance;
           Assert.AreEqual(expected, actual, 0.001, "Account not debited correctly");
       }

Этот метод достаточно прост. Мы создаем новый объект BankAccount с начальным балансом, а затем снимаем допустимое значение. Используем платформу модульных тестов Microsoft для метода AreEqual управляемого кода, чтобы проверить соответствие конечного баланса ожидаемому.

### Требования к методу теста

Метод теста должен удовлетворять следующим требованиям:

- Метод должен быть отмечен атрибутом [TestMethod].

- Метод должен вернуть void.

- Метод не должен иметь параметров.

##
Сборка и запуск теста

### Сборка и запуск теста

1. В меню Построение выберите Построить решение.

   При отсутствии ошибок появляется окно UnitTestExplorer с указанием Debit_WithValidAmount_UpdatesBalance в списке Незапущенные тесты. Если после успешной сборки обозреватель тестов не появляется, выберите в меню Тестирование, затем Окна, а затем Обозреватель тестов.

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

3. В данном случае тест пройден не будет. Метод теста будет перемещен в группу Неудачные тесты. Выберите этот метод в обозревателе тестов для просмотра сведений в нижней части окна.

##
Исправление кода и повторный запуск тестов

Анализ результатов теста

Результат теста содержит сообщение, описывающее возникшую ошибку. Для метода AreEquals сообщение отражает ожидаемый результат (параметр Expected <XXX>) и фактически полученный (параметр Actual<YYY>). Ожидалось, что баланс уменьшится относительно начального баланса, но вместо этого произошло его увеличение на сумму списания.

Повторная проверка кода "Дебит" показывает, что модульный тест позволил успешно найти ошибку. Сумма списания добавляется на баланс счета, а не вычитается.

Исправление ошибки

Для исправления ошибки просто замените строку

C#
Копировать

    m_balance += amount;

на

C#
Копировать

    m_balance -= amount;

Повторный запуск теста

В обозревателе тестов выберите Запустить все, чтобы запустить тест повторно. Красно-зеленая строка состояния станет зеленой, и тест будет перемещен в группу Пройденные тесты.

##
Использование модульных тестов для улучшения кода

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

Анализ проблем

После создания тестового метода, проверяющего корректное списывание нужной суммы в методе Debit, вернемся к остальным вариантам исходного анализа.

1. Метод создает исключение ArgumentOutOfRangeException, если сумма кредита превышает баланс.

2. Он также создает ArgumentOutOfRangeException, если размер кредита меньше нуля.

Создание методов теста

Первая попытка создания тестового метода для решения этих проблем кажется перспективной:

C#
Копировать

    //unit test method
    [TestMethod]
    [ExpectedException(typeof(ArgumentOutOfRangeException))]
    public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange()
    {
        // arrange
        double beginningBalance = 11.99;
        double debitAmount = -100.00;
        BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

        // act
        account.Debit(debitAmount);

        // assert is handled by ExpectedException
    }

Мы используем атрибут ExpectedExceptionAttribute для подтверждения правильности созданного исключения. Данный атрибут приводит к тому, что тест не будет пройден, если не возникнет исключения ArgumentOutOfRangeException. Запуск теста как с положительными, так и с отрицательными значениями debitAmount, а также временное изменение тестируемого метода для возвращения универсального исключения ApplicationException при размере списания меньше нуля показывает, что тест работает правильно. Чтобы проверить случай, когда размер списания превышает баланс, необходимо:

1. Создать новый метод теста с именем Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange.

2. Скопировать тело метода из Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange в новый метод.

3. Присвоить debitAmount значение, превышающее баланс.

Запуск тестов

Запуск двух методов с разными значениями для debitAmount показывает, что тесты корректно обрабатывают оставшиеся варианты. Запуск всех трех тестов позволяет убедиться в том, что все варианты в исходном анализе правильно охвачены тестами.

Продолжение анализа

Однако последние два тестовых метода вызывают некоторое беспокойство. Невозможно точно определить, какое условие тестируемого кода из всех тестовых запусков приводит к возникновению исключения. Желательно иметь способ различения этих двух условий. При дальнейшем анализе данной проблемы становится очевидным, что наличие сведений о нарушенном условии повышает надежность тестов. Также эти сведения, вероятно, могут быть полезны для создания рабочего механизма обработки ошибок при их возникновении в тестируемом методе. Получение дополнительной информации при создании методом исключения позволило бы установить данную связь, но атрибут ExpectedException не предоставляет эту информацию.

Еще раз посмотрев на тестируемый метод, можно заметить, что оба условных оператора используют конструктор ArgumentOutOfRangeException, который получает имя аргумента в качестве параметра:

C#
Копировать

    throw new ArgumentOutOfRangeException("amount");

Поиск в библиотеке MSDN позволяет обнаружить существование конструктора, сообщающего более детальную информацию. ArgumentOutOfRangeException (String, Object, String) включает имя аргумента, значения аргумента и определяемое пользователем сообщение. Мы можем выполнить рефакторинг тестируемого метода для использования данного конструктора. Более того, можно использовать открытые для общего доступа члены типа для указания ошибок.

Рефакторинг тестируемого кода

Сначала определим две константы для сообщений об ошибках в области видимости класса:

C#
Копировать

    // class under test
    public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
    public const string DebitAmountLessThanZeroMessage = "Debit amount less than zero";

Изменим два условных оператора в методе Debit:

C#
Копировать

    // method under test
    // ...
        if (amount > m_balance)
        {
            throw new ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage);
        }

        if (amount < 0)
        {
            throw new ArgumentOutOfRangeException("amount", amount, DebitAmountLessThanZeroMessage);
        }
    // ...

Рефакторинг тестовых методов

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

- Проверить, что свойство ActualValue исключения (второй параметр конструктора ArgumentOutOfRangeException) больше начального баланса. В данном случае необходимо сравнить свойство ActualValue исключения со значением переменной beginningBalance метода теста, а также проверить, что значение ActualValue больше нуля.

- Проверить, что сообщение (третий параметр конструктора) включает DebitAmountExceedsBalanceMessage, определенное в классе BankAccount.

Метод StringAssert.Contains платформы модульных тестов Microsoft позволяет проверить второй параметр без вычислений, необходимых в первом случае.

Вторая попытка корректировки Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange может выглядеть следующим образом:

C#
Копировать

    [TestMethod]
    public void Debit_WhenAmountIsGreaterThanBalance_ShouldThrowArgumentOutOfRange()
    {
        // arrange
        double beginningBalance = 11.99;
        double debitAmount = 20.0;
        BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);\

        // act
        try
        {
            account.Debit(debitAmount);
        }
        catch (ArgumentOutOfRangeException e)
        {
            // assert
            StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage);
        }
    }

Повторное тестирование, переписывание и анализ

При повторной проверке тестируемого метода с разными значениями получаем следующие факты:

1. При верном обнаружении ошибки в случае, если debitAmount больше баланса, проверка утверждения Contains проходит успешно, исключение игнорируется и метод теста завершается успешно. Это соответствует ожидаемому поведению.

2. Если используется debitAmount, то проверка утверждения завершается неудачей, так как возвращается неверное сообщение об ошибке. Проверка утверждения также завершается неудачей, если ввести временное исключение ArgumentOutOfRange на другом шаге в коде тестируемого метода. Это также хорошо.

3. Если значение debitAmount допустимо (т.е. меньше баланса, но больше нуля), то исключение перехватывается, а утверждение никогда не сработает. Метод теста проходит успешно. Это не хорошо, поскольку метод теста должен был завершиться ошибкой в том случае, если исключение не создается.

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

Однако повторное тестирование показывает, что тест теперь оказывается непройденным при перехватывании верного исключения. Оператор catch сбрасывает исключение и метод продолжает выполняться, приводя к ошибочному срабатыванию следующего утверждения. Чтобы разрешить эту новую проблему, добавим оператор return после StringAssert. Повторное тестирование подтверждает, что проблемы решены. Наша окончательная версия Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange выглядит следующим образом:

C#
Копировать

    [TestMethod]
    public void Debit_WhenAmountIsGreaterThanBalance_ShouldThrowArgumentOutOfRange()
    {
        // arrange
        double beginningBalance = 11.99;
        double debitAmount = 20.0;
        BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);\

        // act
        try
        {
            account.Debit(debitAmount);
        }
        catch (ArgumentOutOfRangeException e)
        {
            // assert
            StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage);
            return;
        }
        Assert.Fail("No exception was thrown.")
    }

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

## Добавления сообщества

Показ: Унаследовано Защищен
© 2014 Microsoft
loading...
We then modify the two conditional statements in the Debit method:
Предложите более удачный вариант перевода