Руководство. Тестирование библиотеки классов .NET с помощью .NET с помощью Visual Studio

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

Предварительные условия

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

Модульные тесты обеспечивают автоматическое тестирование программного обеспечения во время разработки и публикации. MSTest — это одна из трех доступных для выбора платформ тестирования. Другими являются xUnit и nUnit.

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

  2. Откройте решениеClassLibraryProjects, созданное при работе со статьей Создание библиотеки классов .NET в Visual Studio.

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

    1. Щелкните решение в обозревателе решений правой кнопкой мыши и выберите Добавить>Новый проект.

    2. На странице Добавить новый проект введите в поле поиска mstest. Выберите C# или Visual Basic из списка языков, а затем — Все платформы из списка платформ.

    3. Выберите шаблон Тестовый проект MSTest и щелкните Далее.

    4. На странице Настроить новый проект введите StringLibraryTest в поле Имя проекта. Теперь щелкните Далее.

    5. На странице "Дополнительные сведения" выберите .NET 8 (предварительная версия) в поле Framework. Щелкните Создать.

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

    namespace StringLibraryTest;
    
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
    
    Imports Microsoft.VisualStudio.TestTools.UnitTesting
    
    Namespace StringLibraryTest
        <TestClass>
        Public Class UnitTest1
            <TestMethod>
            Sub TestSub()
    
            End Sub
        End Class
    End Namespace
    

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

    • Он импортирует пространство имен Microsoft.VisualStudio.TestTools.UnitTesting, которое содержит типы, используемые для модульного тестирования. В C#пространство имен импортируется с помощью global using директивы GlobalUsings.cs.
    • Он применяет атрибут TestClassAttribute к классу UnitTest1.
    • Он применяет атрибут TestMethodAttribute для определения TestMethod1 в C# или TestSub в Visual Basic.

    При запуске модульного теста автоматически выполняются все методы теста, помеченные атрибутом [TestMethod], в тестовом классе, помеченном атрибутом [TestClass].

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

Чтобы тестовый проект работал с классом StringLibrary, добавьте в проект StringLibraryTest ссылку на проект StringLibrary.

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

  2. В диалоговом окне Диспетчер ссылок разверните узел Проекты и установите флажок рядом с пунктом StringLibrary. Добавление ссылки на сборку StringLibrary позволяет компилятору находить методы StringLibrary во время компиляции проекта StringLibraryTest.

  3. Нажмите ОК.

Добавление и выполнение методов модульного теста

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

В самых распространенных тестах вызываются члены класса Assert. Многие методы утверждения (Assert) принимают по крайней мере два параметра, из которых один представляет ожидаемый результат теста, а второй — фактический результат теста. Наиболее популярные из этих методов класса Assert перечислены в следующей таблице:

Методы утверждения Функция
Assert.AreEqual Проверяет равенство двух значений или объектов. Утверждение не выполняется, если значения или объекты не равны.
Assert.AreSame Проверяет, что две объектные переменные ссылаются на один и тот же объект. Утверждение не выполняется, если переменные ссылаются на разные объекты.
Assert.IsFalse Проверяет, что условие имеет значение false. Утверждение не выполняется, если условие имеет значение true.
Assert.IsNotNull Проверяет, что объект не имеет значение null. Утверждение не выполняется, если объект является null.

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

Для тестирования метода StringLibrary.StartsWithUpper необходимо предоставить несколько строк, которые начинаются с символов верхнего регистра. Предполагается, что в этих случаях метод возвратит true, поэтому можно вызвать метод Assert.IsTrue. Представьте также несколько строк, которые не начинаются с символов верхнего регистра. Предполагается, что в этих случаях метод возвратит false, поэтому можно вызвать метод Assert.IsFalse.

Так как ваш метод библиотеки обрабатывает строки, нам нужно проверить правильность обработки пустых строк (String.Empty) (так называется допустимая строка, которая не содержит символов и для которой свойство Length имеет значение 0) и строки null, которая не была инициализирована. Метод StartsWithUpper можно вызвать напрямую как статический метод и передать ему один аргумент типа String. Или метод StartsWithUpper можно вызвать как метод расширения для переменной string, которой назначено значение null.

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

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

  1. В окне кода UnitTest1.cs или UnitTest1.vb замените отображаемый код на следующий текст:

    using UtilityLibraries;
    
    namespace StringLibraryTest
    {
        [TestClass]
        public class UnitTest1
        {
            [TestMethod]
            public void TestStartsWithUpper()
            {
                // Tests that we expect to return true.
                string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" };
                foreach (var word in words)
                {
                    bool result = word.StartsWithUpper();
                    Assert.IsTrue(result,
                           string.Format("Expected for '{0}': true; Actual: {1}",
                                         word, result));
                }
            }
    
            [TestMethod]
            public void TestDoesNotStartWithUpper()
            {
                // Tests that we expect to return false.
                string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                                   "1234", ".", ";", " " };
                foreach (var word in words)
                {
                    bool result = word.StartsWithUpper();
                    Assert.IsFalse(result,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word, result));
                }
            }
    
            [TestMethod]
            public void DirectCallWithNullOrEmpty()
            {
                // Tests that we expect to return false.
                string?[] words = { string.Empty, null };
                foreach (var word in words)
                {
                    bool result = StringLibrary.StartsWithUpper(word);
                    Assert.IsFalse(result,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word == null ? "<null>" : word, result));
                }
            }
        }
    }
    
    Imports Microsoft.VisualStudio.TestTools.UnitTesting
    Imports UtilityLibraries
    
    Namespace StringLibraryTest
        <TestClass>
        Public Class UnitTest1
            <TestMethod>
            Public Sub TestStartsWithUpper()
                ' Tests that we expect to return true.
                Dim words() As String = {"Alphabet", "Zebra", "ABC", "Αθήνα", "Москва"}
                For Each word In words
                    Dim result As Boolean = word.StartsWithUpper()
                    Assert.IsTrue(result,
                           $"Expected for '{word}': true; Actual: {result}")
                Next
            End Sub
    
            <TestMethod>
            Public Sub TestDoesNotStartWithUpper()
                ' Tests that we expect to return false.
                Dim words() As String = {"alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                                   "1234", ".", ";", " "}
                For Each word In words
                    Dim result As Boolean = word.StartsWithUpper()
                    Assert.IsFalse(result,
                           $"Expected for '{word}': false; Actual: {result}")
                Next
            End Sub
    
            <TestMethod>
            Public Sub DirectCallWithNullOrEmpty()
                ' Tests that we expect to return false.
                Dim words() As String = {String.Empty, Nothing}
                For Each word In words
                    Dim result As Boolean = StringLibrary.StartsWithUpper(word)
                    Assert.IsFalse(result,
                           $"Expected for '{If(word Is Nothing, "<null>", word)}': false; Actual: {result}")
                Next
            End Sub
        End Class
    End Namespace
    

    Тест на символы верхнего регистра в методе TestStartsWithUpper включает заглавную греческую букву "альфа" (U+0391) и заглавную кириллическую букву "М" (U+041C). Тест на символы нижнего регистра в методе TestDoesNotStartWithUpper включает строчную греческую букву "альфа" (U+03B1) и строчную кириллическую букву "г" (U+0433).

  2. В строке меню выберите Файл>Сохранить UnitTest1.cs как или Файл>Сохранить UnitTest1.vb как. В диалоговом окне Сохранить файл как щелкните стрелку рядом с кнопкой Сохранить и выберите вариант Сохранить с кодировкой.

    Visual Studio Save File As dialog

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

  4. В диалоговом окне Дополнительные параметры сохранения выберите в раскрывающемся списка Кодировка вариант Юникод (UTF-8, с сигнатурой), кодовая страница 65001 и нажмите кнопку ОК.

    Visual Studio Advanced Save Options dialog

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

  5. В строке меню выберите Тест>Выполнить все тесты. Если окно обозревателя тестов не открыто, откройте его, выбрав Тест>Обозреватель тестов. В разделе Пройденные тесты перечислены три теста, а раздел Сводка содержит результат тестового запуска.

    Test Explorer window with passing tests

Обработка сбоев теста

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

  1. Измените массив words в методе TestDoesNotStartWithUpper, включив в него строку "Error". Сохранять файл не требуется, поскольку при сборке решения для выполнения тестов Visual Studio автоматически сохраняет открытые файлы.

    string[] words = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                       "1234", ".", ";", " " };
    
    Dim words() As String = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                       "1234", ".", ";", " " }
    
    
  2. Выполните тест, последовательно выбрав в строке меню пункты Тест>Выполнить все тесты. В окне Обозреватель тестов будет указано, что два теста выполнены успешно, а третий завершился ошибкой.

    Test Explorer window with failing tests

  3. Выберите непройденный тест TestDoesNotStartWith.

    В окне Обозреватель тестов появится сообщение, созданное методом утверждения: "Assert.IsFalse failed. Expected for 'Error': false; actual: True". Из-за этого сбоя строки в массиве, расположенные после слова "Error", не проверялись.

    Test Explorer window showing the IsFalse assertion failure

  4. Удалите строку "Error", которую вы добавили на шаге 1. Еще раз запустите тест. Теперь тесты будут пройдены.

Тестирование версии выпуска для библиотеки

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

Протестируйте сборку выпуска следующим образом.

  1. В панели инструментов Visual Studio измените конфигурацию сборки с режима Отладка на Выпуск.

    Visual Studio toolbar with release build highlighted

  2. В обозревателе решений щелкните проект StringLibrary правой кнопкой мыши и выберите в контекстном меню пункт Сборка, чтобы выполнить повторную компиляцию библиотеки.

    StringLibrary context menu with build command

  3. Запустите модульные тесты, выбрав "Тест>запустить все тесты" в строке меню. Все тесты будут пройдены.

Отладка тестов

Если вы используете Visual Studio в качестве интегрированной среды разработки, можно использовать тот же процесс, что и в руководстве. Отладка консольного приложения .NET с помощью Visual Studio для отладки кода с помощью проекта модульного теста. Вместо запуска проекта приложения ShowCase щелкните правой кнопкой проект StringLibraryTests и выберите элемент Отладка тестов в контекстном меню.

Visual Studio запускает тестовый проект с присоединенным отладчиком. Выполнение будет прервано в любой точке останова, добавленной в тестовый проект или базовый код библиотеки.

Дополнительные ресурсы

Далее

В этом руководстве вы выполнили модульное тестирование библиотеки классов. Чтобы сделать библиотеку доступной другим пользователям, опубликуйте ее в NuGet в качестве пакета. Чтобы узнать, как это делать, следуйте указаниям в руководстве по NuGet:

Если библиотека опубликована как пакет NuGet, ее могут устанавливать и использовать другие пользователи. Чтобы узнать, как это делать, следуйте указаниям в руководстве по NuGet:

Библиотеку не нужно распространять как пакет. Ее можно объединить с консольным приложением, где она используется. Чтобы узнать, как опубликовать консольное приложение, ознакомьтесь с предыдущим руководством в этой серии:

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

Предварительные условия

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

Модульные тесты обеспечивают автоматическое тестирование программного обеспечения во время разработки и публикации. MSTest — это одна из трех доступных для выбора платформ тестирования. Другими являются xUnit и nUnit.

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

  2. Откройте решениеClassLibraryProjects, созданное при работе со статьей Создание библиотеки классов .NET в Visual Studio.

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

    1. Щелкните решение в обозревателе решений правой кнопкой мыши и выберите Добавить>Новый проект.

    2. На странице Добавить новый проект введите в поле поиска mstest. Выберите C# или Visual Basic из списка языков, а затем — Все платформы из списка платформ.

    3. Выберите шаблон Тестовый проект MSTest и щелкните Далее.

    4. На странице Настроить новый проект введите StringLibraryTest в поле Имя проекта. Теперь щелкните Далее.

    5. На странице "Дополнительные сведения" выберите .NET 7 (поддержка со стандартным термином) в поле Framework. Щелкните Создать.

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

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    
    namespace StringLibraryTest
    {
        [TestClass]
        public class UnitTest1
        {
            [TestMethod]
            public void TestMethod1()
            {
            }
        }
    }
    
    Imports Microsoft.VisualStudio.TestTools.UnitTesting
    
    Namespace StringLibraryTest
        <TestClass>
        Public Class UnitTest1
            <TestMethod>
            Sub TestSub()
    
            End Sub
        End Class
    End Namespace
    

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

    • Он импортирует пространство имен Microsoft.VisualStudio.TestTools.UnitTesting, которое содержит типы, используемые для модульного тестирования.
    • Он применяет атрибут TestClassAttribute к классу UnitTest1.
    • Он применяет атрибут TestMethodAttribute для определения TestMethod1 в C# или TestSub в Visual Basic.

    При запуске модульного теста автоматически выполняются все методы теста, помеченные атрибутом [TestMethod], в тестовом классе, помеченном атрибутом [TestClass].

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

Чтобы тестовый проект работал с классом StringLibrary, добавьте в проект StringLibraryTest ссылку на проект StringLibrary.

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

  2. В диалоговом окне Диспетчер ссылок разверните узел Проекты и установите флажок рядом с пунктом StringLibrary. Добавление ссылки на сборку StringLibrary позволяет компилятору находить методы StringLibrary во время компиляции проекта StringLibraryTest.

  3. Нажмите ОК.

Добавление и выполнение методов модульного теста

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

В самых распространенных тестах вызываются члены класса Assert. Многие методы утверждения (Assert) принимают по крайней мере два параметра, из которых один представляет ожидаемый результат теста, а второй — фактический результат теста. Наиболее популярные из этих методов класса Assert перечислены в следующей таблице:

Методы утверждения Функция
Assert.AreEqual Проверяет равенство двух значений или объектов. Утверждение не выполняется, если значения или объекты не равны.
Assert.AreSame Проверяет, что две объектные переменные ссылаются на один и тот же объект. Утверждение не выполняется, если переменные ссылаются на разные объекты.
Assert.IsFalse Проверяет, что условие имеет значение false. Утверждение не выполняется, если условие имеет значение true.
Assert.IsNotNull Проверяет, что объект не имеет значение null. Утверждение не выполняется, если объект является null.

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

Для тестирования метода StringLibrary.StartsWithUpper необходимо предоставить несколько строк, которые начинаются с символов верхнего регистра. Предполагается, что в этих случаях метод возвратит true, поэтому можно вызвать метод Assert.IsTrue. Представьте также несколько строк, которые не начинаются с символов верхнего регистра. Предполагается, что в этих случаях метод возвратит false, поэтому можно вызвать метод Assert.IsFalse.

Так как ваш метод библиотеки обрабатывает строки, нам нужно проверить правильность обработки пустых строк (String.Empty) (так называется допустимая строка, которая не содержит символов и для которой свойство Length имеет значение 0) и строки null, которая не была инициализирована. Метод StartsWithUpper можно вызвать напрямую как статический метод и передать ему один аргумент типа String. Или метод StartsWithUpper можно вызвать как метод расширения для переменной string, которой назначено значение null.

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

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

  1. В окне кода UnitTest1.cs или UnitTest1.vb замените отображаемый код на следующий текст:

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using UtilityLibraries;
    
    namespace StringLibraryTest
    {
        [TestClass]
        public class UnitTest1
        {
            [TestMethod]
            public void TestStartsWithUpper()
            {
                // Tests that we expect to return true.
                string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" };
                foreach (var word in words)
                {
                    bool result = word.StartsWithUpper();
                    Assert.IsTrue(result,
                           string.Format("Expected for '{0}': true; Actual: {1}",
                                         word, result));
                }
            }
    
            [TestMethod]
            public void TestDoesNotStartWithUpper()
            {
                // Tests that we expect to return false.
                string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                                   "1234", ".", ";", " " };
                foreach (var word in words)
                {
                    bool result = word.StartsWithUpper();
                    Assert.IsFalse(result,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word, result));
                }
            }
    
            [TestMethod]
            public void DirectCallWithNullOrEmpty()
            {
                // Tests that we expect to return false.
                string?[] words = { string.Empty, null };
                foreach (var word in words)
                {
                    bool result = StringLibrary.StartsWithUpper(word);
                    Assert.IsFalse(result,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word == null ? "<null>" : word, result));
                }
            }
        }
    }
    
    Imports Microsoft.VisualStudio.TestTools.UnitTesting
    Imports UtilityLibraries
    
    Namespace StringLibraryTest
        <TestClass>
        Public Class UnitTest1
            <TestMethod>
            Public Sub TestStartsWithUpper()
                ' Tests that we expect to return true.
                Dim words() As String = {"Alphabet", "Zebra", "ABC", "Αθήνα", "Москва"}
                For Each word In words
                    Dim result As Boolean = word.StartsWithUpper()
                    Assert.IsTrue(result,
                           $"Expected for '{word}': true; Actual: {result}")
                Next
            End Sub
    
            <TestMethod>
            Public Sub TestDoesNotStartWithUpper()
                ' Tests that we expect to return false.
                Dim words() As String = {"alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                                   "1234", ".", ";", " "}
                For Each word In words
                    Dim result As Boolean = word.StartsWithUpper()
                    Assert.IsFalse(result,
                           $"Expected for '{word}': false; Actual: {result}")
                Next
            End Sub
    
            <TestMethod>
            Public Sub DirectCallWithNullOrEmpty()
                ' Tests that we expect to return false.
                Dim words() As String = {String.Empty, Nothing}
                For Each word In words
                    Dim result As Boolean = StringLibrary.StartsWithUpper(word)
                    Assert.IsFalse(result,
                           $"Expected for '{If(word Is Nothing, "<null>", word)}': false; Actual: {result}")
                Next
            End Sub
        End Class
    End Namespace
    

    Тест на символы верхнего регистра в методе TestStartsWithUpper включает заглавную греческую букву "альфа" (U+0391) и заглавную кириллическую букву "М" (U+041C). Тест на символы нижнего регистра в методе TestDoesNotStartWithUpper включает строчную греческую букву "альфа" (U+03B1) и строчную кириллическую букву "г" (U+0433).

  2. В строке меню выберите Файл>Сохранить UnitTest1.cs как или Файл>Сохранить UnitTest1.vb как. В диалоговом окне Сохранить файл как щелкните стрелку рядом с кнопкой Сохранить и выберите вариант Сохранить с кодировкой.

    Visual Studio Save File As dialog

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

  4. В диалоговом окне Дополнительные параметры сохранения выберите в раскрывающемся списка Кодировка вариант Юникод (UTF-8, с сигнатурой), кодовая страница 65001 и нажмите кнопку ОК.

    Visual Studio Advanced Save Options dialog

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

  5. В строке меню выберите Тест>Выполнить все тесты. Если окно обозревателя тестов не открыто, откройте его, выбрав Тест>Обозреватель тестов. В разделе Пройденные тесты перечислены три теста, а раздел Сводка содержит результат тестового запуска.

    Test Explorer window with passing tests

Обработка сбоев теста

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

  1. Измените массив words в методе TestDoesNotStartWithUpper, включив в него строку "Error". Сохранять файл не требуется, поскольку при сборке решения для выполнения тестов Visual Studio автоматически сохраняет открытые файлы.

    string[] words = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                       "1234", ".", ";", " " };
    
    Dim words() As String = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                       "1234", ".", ";", " " }
    
    
  2. Выполните тест, последовательно выбрав в строке меню пункты Тест>Выполнить все тесты. В окне Обозреватель тестов будет указано, что два теста выполнены успешно, а третий завершился ошибкой.

    Test Explorer window with failing tests

  3. Выберите непройденный тест TestDoesNotStartWith.

    В окне Обозреватель тестов появится сообщение, созданное методом утверждения: "Assert.IsFalse failed. Expected for 'Error': false; actual: True". Из-за этого сбоя строки в массиве, расположенные после слова "Error", не проверялись.

    Test Explorer window showing the IsFalse assertion failure

  4. Удалите строку "Error", которую вы добавили на шаге 1. Еще раз запустите тест. Теперь тесты будут пройдены.

Тестирование версии выпуска для библиотеки

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

Протестируйте сборку выпуска следующим образом.

  1. В панели инструментов Visual Studio измените конфигурацию сборки с режима Отладка на Выпуск.

    Visual Studio toolbar with release build highlighted

  2. В обозревателе решений щелкните проект StringLibrary правой кнопкой мыши и выберите в контекстном меню пункт Сборка, чтобы выполнить повторную компиляцию библиотеки.

    StringLibrary context menu with build command

  3. Запустите модульные тесты, выбрав "Тест>запустить все тесты" в строке меню. Все тесты будут пройдены.

Отладка тестов

Если вы используете Visual Studio в качестве интегрированной среды разработки, можно использовать тот же процесс, что и в руководстве. Отладка консольного приложения .NET с помощью Visual Studio для отладки кода с помощью проекта модульного теста. Вместо запуска проекта приложения ShowCase щелкните правой кнопкой проект StringLibraryTests и выберите элемент Отладка тестов в контекстном меню.

Visual Studio запускает тестовый проект с присоединенным отладчиком. Выполнение будет прервано в любой точке останова, добавленной в тестовый проект или базовый код библиотеки.

Дополнительные ресурсы

Далее

В этом руководстве вы выполнили модульное тестирование библиотеки классов. Чтобы сделать библиотеку доступной другим пользователям, опубликуйте ее в NuGet в качестве пакета. Чтобы узнать, как это делать, следуйте указаниям в руководстве по NuGet:

Если библиотека опубликована как пакет NuGet, ее могут устанавливать и использовать другие пользователи. Чтобы узнать, как это делать, следуйте указаниям в руководстве по NuGet:

Библиотеку не нужно распространять как пакет. Ее можно объединить с консольным приложением, где она используется. Чтобы узнать, как опубликовать консольное приложение, ознакомьтесь с предыдущим руководством в этой серии:

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

Предварительные условия

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

Модульные тесты обеспечивают автоматическое тестирование программного обеспечения во время разработки и публикации. MSTest — это одна из трех доступных для выбора платформ тестирования. Другими являются xUnit и nUnit.

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

  2. Откройте решениеClassLibraryProjects, созданное при работе со статьей Создание библиотеки классов .NET в Visual Studio.

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

    1. Щелкните решение в обозревателе решений правой кнопкой мыши и выберите Добавить>Новый проект.

    2. На странице Добавить новый проект введите в поле поиска mstest. Выберите C# или Visual Basic из списка языков, а затем — Все платформы из списка платформ.

    3. Выберите шаблон Тестовый проект MSTest и щелкните Далее.

    4. На странице Настроить новый проект введите StringLibraryTest в поле Имя проекта. Теперь щелкните Далее.

    5. На странице Дополнительные сведения выберите .NET 6 (долгосрочная поддержка) в поле Платформа. Щелкните Создать.

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

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    
    namespace StringLibraryTest
    {
        [TestClass]
        public class UnitTest1
        {
            [TestMethod]
            public void TestMethod1()
            {
            }
        }
    }
    
    Imports Microsoft.VisualStudio.TestTools.UnitTesting
    
    Namespace StringLibraryTest
        <TestClass>
        Public Class UnitTest1
            <TestMethod>
            Sub TestSub()
    
            End Sub
        End Class
    End Namespace
    

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

    • Он импортирует пространство имен Microsoft.VisualStudio.TestTools.UnitTesting, которое содержит типы, используемые для модульного тестирования.
    • Он применяет атрибут TestClassAttribute к классу UnitTest1.
    • Он применяет атрибут TestMethodAttribute для определения TestMethod1 в C# или TestSub в Visual Basic.

    При запуске модульного теста автоматически выполняются все методы теста, помеченные атрибутом [TestMethod], в тестовом классе, помеченном атрибутом [TestClass].

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

Чтобы тестовый проект работал с классом StringLibrary, добавьте в проект StringLibraryTest ссылку на проект StringLibrary.

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

  2. В диалоговом окне Диспетчер ссылок разверните узел Проекты и установите флажок рядом с пунктом StringLibrary. Добавление ссылки на сборку StringLibrary позволяет компилятору находить методы StringLibrary во время компиляции проекта StringLibraryTest.

  3. Нажмите ОК.

Добавление и выполнение методов модульного теста

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

В самых распространенных тестах вызываются члены класса Assert. Многие методы утверждения (Assert) принимают по крайней мере два параметра, из которых один представляет ожидаемый результат теста, а второй — фактический результат теста. Наиболее популярные из этих методов класса Assert перечислены в следующей таблице:

Методы утверждения Функция
Assert.AreEqual Проверяет равенство двух значений или объектов. Утверждение не выполняется, если значения или объекты не равны.
Assert.AreSame Проверяет, что две объектные переменные ссылаются на один и тот же объект. Утверждение не выполняется, если переменные ссылаются на разные объекты.
Assert.IsFalse Проверяет, что условие имеет значение false. Утверждение не выполняется, если условие имеет значение true.
Assert.IsNotNull Проверяет, что объект не имеет значение null. Утверждение не выполняется, если объект является null.

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

Для тестирования метода StringLibrary.StartsWithUpper необходимо предоставить несколько строк, которые начинаются с символов верхнего регистра. Предполагается, что в этих случаях метод возвратит true, поэтому можно вызвать метод Assert.IsTrue. Представьте также несколько строк, которые не начинаются с символов верхнего регистра. Предполагается, что в этих случаях метод возвратит false, поэтому можно вызвать метод Assert.IsFalse.

Так как ваш метод библиотеки обрабатывает строки, нам нужно проверить правильность обработки пустых строк (String.Empty) (так называется допустимая строка, которая не содержит символов и для которой свойство Length имеет значение 0) и строки null, которая не была инициализирована. Метод StartsWithUpper можно вызвать напрямую как статический метод и передать ему один аргумент типа String. Или метод StartsWithUpper можно вызвать как метод расширения для переменной string, которой назначено значение null.

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

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

  1. В окне кода UnitTest1.cs или UnitTest1.vb замените отображаемый код на следующий текст:

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using UtilityLibraries;
    
    namespace StringLibraryTest
    {
        [TestClass]
        public class UnitTest1
        {
            [TestMethod]
            public void TestStartsWithUpper()
            {
                // Tests that we expect to return true.
                string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" };
                foreach (var word in words)
                {
                    bool result = word.StartsWithUpper();
                    Assert.IsTrue(result,
                           string.Format("Expected for '{0}': true; Actual: {1}",
                                         word, result));
                }
            }
    
            [TestMethod]
            public void TestDoesNotStartWithUpper()
            {
                // Tests that we expect to return false.
                string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                                   "1234", ".", ";", " " };
                foreach (var word in words)
                {
                    bool result = word.StartsWithUpper();
                    Assert.IsFalse(result,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word, result));
                }
            }
    
            [TestMethod]
            public void DirectCallWithNullOrEmpty()
            {
                // Tests that we expect to return false.
                string?[] words = { string.Empty, null };
                foreach (var word in words)
                {
                    bool result = StringLibrary.StartsWithUpper(word);
                    Assert.IsFalse(result,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word == null ? "<null>" : word, result));
                }
            }
        }
    }
    
    Imports Microsoft.VisualStudio.TestTools.UnitTesting
    Imports UtilityLibraries
    
    Namespace StringLibraryTest
        <TestClass>
        Public Class UnitTest1
            <TestMethod>
            Public Sub TestStartsWithUpper()
                ' Tests that we expect to return true.
                Dim words() As String = {"Alphabet", "Zebra", "ABC", "Αθήνα", "Москва"}
                For Each word In words
                    Dim result As Boolean = word.StartsWithUpper()
                    Assert.IsTrue(result,
                           $"Expected for '{word}': true; Actual: {result}")
                Next
            End Sub
    
            <TestMethod>
            Public Sub TestDoesNotStartWithUpper()
                ' Tests that we expect to return false.
                Dim words() As String = {"alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                                   "1234", ".", ";", " "}
                For Each word In words
                    Dim result As Boolean = word.StartsWithUpper()
                    Assert.IsFalse(result,
                           $"Expected for '{word}': false; Actual: {result}")
                Next
            End Sub
    
            <TestMethod>
            Public Sub DirectCallWithNullOrEmpty()
                ' Tests that we expect to return false.
                Dim words() As String = {String.Empty, Nothing}
                For Each word In words
                    Dim result As Boolean = StringLibrary.StartsWithUpper(word)
                    Assert.IsFalse(result,
                           $"Expected for '{If(word Is Nothing, "<null>", word)}': false; Actual: {result}")
                Next
            End Sub
        End Class
    End Namespace
    

    Тест на символы верхнего регистра в методе TestStartsWithUpper включает заглавную греческую букву "альфа" (U+0391) и заглавную кириллическую букву "М" (U+041C). Тест на символы нижнего регистра в методе TestDoesNotStartWithUpper включает строчную греческую букву "альфа" (U+03B1) и строчную кириллическую букву "г" (U+0433).

  2. В строке меню выберите Файл>Сохранить UnitTest1.cs как или Файл>Сохранить UnitTest1.vb как. В диалоговом окне Сохранить файл как щелкните стрелку рядом с кнопкой Сохранить и выберите вариант Сохранить с кодировкой.

    Visual Studio Save File As dialog

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

  4. В диалоговом окне Дополнительные параметры сохранения выберите в раскрывающемся списка Кодировка вариант Юникод (UTF-8, с сигнатурой), кодовая страница 65001 и нажмите кнопку ОК.

    Visual Studio Advanced Save Options dialog

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

  5. В строке меню выберите Тест>Выполнить все тесты. Если окно обозревателя тестов не открыто, откройте его, выбрав Тест>Обозреватель тестов. В разделе Пройденные тесты перечислены три теста, а раздел Сводка содержит результат тестового запуска.

    Test Explorer window with passing tests

Обработка сбоев теста

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

  1. Измените массив words в методе TestDoesNotStartWithUpper, включив в него строку "Error". Сохранять файл не требуется, поскольку при сборке решения для выполнения тестов Visual Studio автоматически сохраняет открытые файлы.

    string[] words = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                       "1234", ".", ";", " " };
    
    Dim words() As String = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                       "1234", ".", ";", " " }
    
    
  2. Выполните тест, последовательно выбрав в строке меню пункты Тест>Выполнить все тесты. В окне Обозреватель тестов будет указано, что два теста выполнены успешно, а третий завершился ошибкой.

    Test Explorer window with failing tests

  3. Выберите непройденный тест TestDoesNotStartWith.

    В окне Обозреватель тестов появится сообщение, созданное методом утверждения: "Assert.IsFalse failed. Expected for 'Error': false; actual: True". Из-за этого сбоя строки в массиве, расположенные после слова "Error", не проверялись.

    Test Explorer window showing the IsFalse assertion failure

  4. Удалите строку "Error", которую вы добавили на шаге 1. Еще раз запустите тест. Теперь тесты будут пройдены.

Тестирование версии выпуска для библиотеки

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

Протестируйте сборку выпуска следующим образом.

  1. В панели инструментов Visual Studio измените конфигурацию сборки с режима Отладка на Выпуск.

    Visual Studio toolbar with release build highlighted

  2. В обозревателе решений щелкните проект StringLibrary правой кнопкой мыши и выберите в контекстном меню пункт Сборка, чтобы выполнить повторную компиляцию библиотеки.

    StringLibrary context menu with build command

  3. Запустите модульные тесты, выбрав "Тест>запустить все тесты" в строке меню. Все тесты будут пройдены.

Отладка тестов

Если вы используете Visual Studio в качестве интегрированной среды разработки, можно использовать тот же процесс, что и в руководстве. Отладка консольного приложения .NET с помощью Visual Studio для отладки кода с помощью проекта модульного теста. Вместо запуска проекта приложения ShowCase щелкните правой кнопкой проект StringLibraryTests и выберите элемент Отладка тестов в контекстном меню.

Visual Studio запускает тестовый проект с присоединенным отладчиком. Выполнение будет прервано в любой точке останова, добавленной в тестовый проект или базовый код библиотеки.

Дополнительные ресурсы

Далее

В этом руководстве вы выполнили модульное тестирование библиотеки классов. Чтобы сделать библиотеку доступной другим пользователям, опубликуйте ее в NuGet в качестве пакета. Чтобы узнать, как это делать, следуйте указаниям в руководстве по NuGet:

Если библиотека опубликована как пакет NuGet, ее могут устанавливать и использовать другие пользователи. Чтобы узнать, как это делать, следуйте указаниям в руководстве по NuGet:

Библиотеку не нужно распространять как пакет. Ее можно объединить с консольным приложением, где она используется. Чтобы узнать, как опубликовать консольное приложение, ознакомьтесь с предыдущим руководством в этой серии: