Модульное тестирование кода Visual C# в приложениях для Магазина Windows

В этом разделе описан один из способов создания модульных тестов для класса Visual C# в приложении для Магазина Windows (также называемом приложением для Магазина Windows) с помощью Visual Studio Express 2012 для Windows 8 и среды модульного тестирования Майкрософт для С++. Класс Rooter демонстрирует концепции теории пределов из математического анализа за счет реализации функции, которая вычисляет оценку квадратного корня из заданного числа. Приложение Maths затем может использовать эту функцию для представления интересных операций, которые можно выполнять с помощью математических функций.

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

В этом разделе также создается одно решение Visual Studio и отдельные проекты для модульных тестов и для тестируемой библиотеки DLL. Модульные тесты можно включить непосредственно в проект библиотеки DLL или создать отдельные решения для модульных тестов и для DLL.

Примечание

Visual Studio Ultimate, VS Premium и VS Professional предоставляет дополнительные функции для модульного тестирования.

  • В VS Ultimate, VS Premium и VS Professional можно использовать любые сторонние среды модульного тестирования или среды с открытым кодом, для которых создан адаптер надстройки для Microsoft Test Explorer. Кроме того, можно анализировать и отображать данные о покрытии кода для тестов.

  • В VS Ultimate и VS Premium можно запускать тесты после каждого построения.

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

Дополнительные сведения см. в разделе Verifying Code by Using Unit Tests в библиотеке MSDN.

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

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

С помощью обозревателя тестов проверьте, что тесты выполняются

Добавьте в проект Maths класс Rooter

Объедините проект теста с проектом приложения

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

Отладка непройденного теста

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

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

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

  2. В диалоговом окне Новый проект разверните узел Установленные, узел Visual C# и выберите Магазин Windows. В списке шаблонов проектов выберите Пустое приложение.

  3. Назовите проект Maths и проверьте, что установлен флажок Создать каталог для решения.

  4. В обозревателе решений выберите имя решения, в контекстном меню выберите команду Добавить и выберите пункт Новый проект.

  5. В диалоговом окне Новый проект разверните узел Установленные, узел Visual C# и выберите Магазин Windows. В списке шаблонов проектов выберите Библиотека модульных тестов (приложения для Магазина Windows).

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

  6. В редакторе Visual Studio откройте файл UnitTest1.cs.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
    using Maths;
    
    namespace RooterTests
    {
        [TestClass]
        public class UnitTest1
    
            [TestMethod]
            public void TestMethod1()
            {
    
            }
    

    Обратите внимание на следующее.

    1. Каждый тест определен с помощью [TestMethod]. Метод теста должен возвращать значение типа void и не может иметь параметров.

    2. Методы теста должны находиться в классе, обозначенном атрибутом [TestClass].

      При запуске тестов создается экземпляр каждого класса теста. Методы теста вызываются в неопределенном порядке.

    3. Можно задать особые методы, которые вызываются до и после каждого модуля, класса или метода. Дополнительные сведения см. в разделе Использование членов Microsoft.VisualStudio.TestTools.UnitTesting в модульных тестах в библиотеке MSDN.

С помощью обозревателя тестов проверьте, что тесты выполняются

  1. Добавьте некоторый код теста в TestMethod1 файла UnitTest1.cs:

    [TestMethod]
    public void TestMethod1()
    {
        Assert.AreEqual(0, 0);
    }
    

    Обратите внимание, что класс Assert содержит несколько статических методов, которые можно использовать для проверки результатов в методах теста.

  2. В меню Тест выберите Выполнить, а затем выберите Запустить все.

    Будет построен и запущен проект теста. Появится окно обозревателя тестов, а тест будет указан в разделе Пройденные тесты. Область сводки в нижней части окна содержит дополнительные сведения о выбранном тесте.

    Обозреватель тестов

Добавьте в проект Maths класс Rooter

  1. В обозревателе решений выберите имя проекта Maths. В контекстном меню выберите команду Добавить, а затем — Класс.

  2. Назовите файл класса Rooter.cs

  3. Добавьте следующий код в файл класса Rooter.cs:

        public Rooter()
        {
        }
    
        // estimate the square root of a number
        public double SquareRoot(double x)
        {
            return 0.0;
        }
    
    
    

    Класс Rooter объявляет конструктор и метод оценки SqareRoot.

  4. Метод SqareRoot представляет собой минимальную реализацию, достаточную для проверки базовой структуры тестирования.

Объедините проект теста с проектом приложения

  1. Добавьте ссылку на приложение Maths в проект RooterTests.

    1. В обозревателе решений выберите проект RooterTests и в контекстном меню выберите команду Добавить ссылку....

    2. В диалоговом окне Добавить ссылку - RooterTests разверните узел Решение и выберите Проекты. Затем выберите элемент Maths.

      Добавление ссылки на проект Maths

  2. Добавьте оператор using в файл UnitTest1.cs.

    1. Откройте файл UnitTest1.cs.

    2. Добавьте следующий код ниже строки using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;:

      using Maths;
      
  3. Добавьте тест, который использует функцию Rooter. Добавьте следующий код в файл UnitTest1.cpp:

        [TestMethod]
        public void BasicTest()
        {
            Maths.Rooter rooter = new Rooter();
            double expected = 0.0;
            double actual = rooter.SquareRoot(expected * expected);
            double tolerance = .001;
            Assert.AreEqual(expected, actual, tolerance);
        }
    
  4. Выполните построение решения.

    Новый тест появится в обозревателе тестов в узле Незапускавшиеся тесты.

  5. В обозревателе тестов выберите Запустить все.

    Основной тест пройден

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

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

  1. Добавьте новый тест:

        [TestMethod]
        public void RangeTest()
        {
            Rooter rooter = new Rooter();
            for (double v = 1e-6; v < 1e6; v = v * 3.2)
            {
                double expected = v;
                double actual = rooter.SquareRoot(v*v);
                double tolerance = ToleranceHelper(expected);
                Assert.AreEqual(expected, actual, tolerance);
            }
        }
    

    Совет

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

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

  2. В обозревателе тестов выберите Запустить все.

  3. Тест не пройден.

    Сбой теста RangeTest

    Совет

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

  4. Измените код теста, чтобы новый тест был пройден. Измените функцию SqareRoot в файле Rooter.cs:

        public double SquareRoot(double x)
        {
            double estimate = x;
            double diff = x;
            while (diff > estimate / 1000)
            {
                double previousEstimate = estimate;
                estimate = estimate - (estimate * estimate - x) / (2 * estimate);
                diff = Math.Abs(previousEstimate - estimate);
            }
            return estimate;
        }
    
  5. Постройте решение, а затем в обозревателе тестов выберите Запустить все.

    Теперь все три теста проходятся.

Совет

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

Отладка непройденного теста

  1. Добавьте в файл UnitTest1.cs еще один тест:

        // Verify that negative inputs throw an exception.
        [TestMethod]
        public void NegativeRangeTest()
        {
            string message;
            Rooter rooter = new Rooter();
            for (double v = -0.1; v > -3.0; v = v - 0.5)
            {
                try
                {
                    // Should raise an exception:
                    double actual = rooter.SquareRoot(v);
    
                    message = String.Format("No exception for input {0}", v);
                    Assert.Fail(message);
                }
                catch (ArgumentOutOfRangeException ex)
                {
                    continue; // Correct exception.
                }
                catch (Exception e)
                {
                    message = String.Format("Incorrect exception for {0}", v);
                    Assert.Fail(message);
                }
            }
        }
    
  2. В обозревателе тестов выберите Запустить все.

    Тест не пройден. Выберите имя теста в обозревателе тестов. Утверждение, вызвавшее сбой, будет выделено. Сообщение о сбое отображается в области сведений обозревателя тестов.

    Сбой тестов NegativeRangeTests

  3. Чтобы определить причину сбоя теста, пошагово выполните функцию.

    1. Установите точку останова перед функцией SquareRoot.

    2. В контекстном меню непройденного теста выберите Отладить выбранные тесты.

      При остановке выполнения на точке останова выполните код пошагово.

    3. Добавьте в метод Rooter код для перехвата исключения:

          public double SquareRoot(double x)
          {
              if (x < 0.0)
              {
                  throw new ArgumentOutOfRangeException();
          }
      
    1. В обозревателе тестов выберите Запустить все, чтобы протестировать исправленный метод и убедиться, что не была добавлена регрессия.

Теперь все тесты проходятся.

Все тесты пройдены

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

Упростите основной расчет функции SquareRoot.

  1. Измените реализацию результата

    // old code
    //result = result - (result*result - v)/(2*result);
    // new code
    result = (result + v/result) / 2.0;
    
  2. Выберите команду Запустить все, чтобы протестировать подвергнутый рефакторингу метод и убедиться, что не была добавлена регрессия.

Совет

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

Выполните рефакторинг кода теста, чтобы исключить дублирование кода.

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

  1. Добавьте к классу Unit1Test закрытый метод для вычисления значения отклонения, а затем вызывайте этот метод.

        private double ToleranceHelper(double expected)
        {
            return expected / 1000;
        }
    
        ...
    
        [TestMethod]
        public void RangeTest()
        {
            ...
            // old code
            // double tolerance = expected/1000;
            // new code
            double tolerance = ToleranceHelper(expected);
            Assert.AreEqual(expected, actual, tolerance);
        }
        ...
    
  2. Выберите команду Запустить все, чтобы протестировать подвергнутый рефакторингу метод и убедиться, что не была добавлена ошибка.

Примечание

Для добавления в тестовый класс вспомогательного метода не добавляйте в метод атрибут [TestMethod]. Обозреватель тестов не регистрирует метод, который будет запущен.