Это отрывок из электронной книги «Единичное тестирование » Марка Клифтона, любезно предоставленный Syncfusion.
В следующей таблице сопоставляются атрибуты, используемые для написания тестов с NUnit с Visual Studio:
Атрибут NUnit |
Атрибут Visual Studio |
Описание |
---|---|---|
TestFixture |
TestClass |
Определяет тестовый прибор |
Тестовое задание |
Метод испытания |
Определяет метод теста в классе тестового прибора |
TestFixtureSetUp |
ClassInitialize |
Задает код, который запускается перед запуском всех методов тестирования в тестовом приборе. |
TestFixtureTearDown |
ClassCleanup |
Задает код, который запускается после завершения всех тестов в приборе. |
Настроить |
TestInitialize |
Определяет код для запуска перед выполнением каждого теста. |
Срывать |
TestCleanup |
Определяет код для запуска при завершении каждого теста. |
SetUpFixture (см. Ниже) |
AssemblyInitialize |
Задает код, который запускается при загрузке сборки, содержащей все тестовые приборы. |
SetUpFixture (см. Ниже) |
AssemblyCleanup |
Задает код, который запускается при выгрузке сборки, содержащей все тестовые приборы. |
игнорировать |
игнорировать |
Игнорирует конкретный тест. |
Описание (также относится к тестовым приборам). |
Описание |
Описание метода испытаний. В NUnit этот атрибут также может украшать тестовое устройство. |
Следующие атрибуты Visual Studio не соответствуют никаким атрибутам NUnit:
- владелец
- DeploymentItem
- HostType
- приоритет
- Рабочий элемент
- CssIteration
- CssProjectStructure
- TestProperty
Атрибут SetUpFixture
Атрибут SetUpFixture, который применяется к классам, отличается от AssemblyInitialize и AssemblyCleanup в Visual Studio, поскольку он применяется к осветителям в данном пространстве имен. Если все ваши классы тестовых приборов находятся в одном и том же пространстве имен, то этот атрибут ведет себя аналогично AssemblyInitialize и AssemblyCleanup. Учитывая код:
пространство имен UnitTestExamplesNUnit { [SetUpFixture] открытый класс SetupFixtureForNamespace { [Настроить] public void RunBeforeAllFixtures () { Console.WriteLine («Перед всеми тестовыми приборами.»); } [Срывать] public void RunAfterAllFixtures () { Console.WriteLine («После всех тестовых приспособлений.»); } } [TestFixture] открытый класс SetupTeardownFlow { [TestFixtureSetUp] public void SetupFixture () { Console.WriteLine («Настройка прибора»); } [TestFixtureTearDown] public void TeardownFixture () { Console.WriteLine («Срыв прибора.»); } [Настроить] public void SetupTest () { Console.WriteLine («Настройка теста.»); } [Срывать] public void TeardownTest () { Console.WriteLine («Тестовый разрыв.»); } [Тестовое задание] публичный void TestA () { Console.WriteLine («Тест А.»); } [Тестовое задание] public void TestB () { Console.WriteLine («Тест Б.»); } } }
В результате получается:
Перед всеми испытаниями светильников. Настройка прибора. ***** UnitTestExamplesNUnit.SetupTeardownFlow.TestA Испытательная установка. Тест А. Тест Разрушение. ***** UnitTestExamplesNUnit.SetupTeardownFlow.TestB Испытательная установка. Тест Б. Тест Разрушение. Fixture Teardown. После всех испытаний светильников.
Тем не менее, добавив еще одно пространство имен:
пространство имен AnotherNamespace { [SetUpFixture] открытый класс AnotherSetupFixtureForNamespace { [Настроить] public void RunBeforeAllFixtures () { Console.WriteLine («Еще один перед всеми тестовыми приборами.»); } [Срывать] public void RunAfterAllFixtures () { Console.WriteLine («Другое после всех тестовых приспособлений.»); } } }
Еще один перед всеми тестовыми приборами. Еще один после всех тестовых приспособлений. Перед всеми испытаниями светильников. Настройка прибора. ***** UnitTestExamplesNUnit.SetupTeardownFlow.TestA Испытательная установка. Тест А. Тест Разрушение. ***** UnitTestExamplesNUnit.SetupTeardownFlow.TestB Испытательная установка. Тест Б. Тест Разрушение. Fixture Teardown. После всех испытаний светильников.
Возможность выполнения кода, специфичного для контекста пространства имен, имеет то преимущество, что может организовывать набор тестов в разных приборах, но все в одном и том же пространстве имен, которые требуют определенной настройки и процесса разборки.
Также см. «Действия сборки» в разделе «Определенные пользователем атрибуты действий» в следующем разделе.
Дополнительные атрибуты NUnit
NUnit обладает обширным набором атрибутов, которые предоставляют значительную дополнительную функциональность для модульного тестирования.
Тестовая группировка и контроль
- категория
- свита
- Явный
- Тайм-аут
категория
Атрибут Category позволяет группировать тесты и запускать тесты в выбранных категориях. Этот атрибут может применяться к тестам в приборе, а также к отдельным тестам в приборе. Определенные категории могут быть выбраны в консоли приложения, используя аргументы / include и / exclude, или в программе запуска GUI:
Вкладка для выбора категорий для включения или исключения находится на левом краю бегунка GUI.
свита
Атрибут Suite предоставляет возможность программно определять тестовые устройства, которые должен тестировать участник консоли. Эта опция доступна только в консоли. Концепция запуска только определенных тестов или наборов тестов (приспособлений) лучше поддерживается атрибутом Category, описанным ранее.
Явный
Этот атрибут обозначает, что тест запускается только в том случае, если графическому интерфейсу или консольному исполнителю явно сказано выполнить тест, выбрав тест. Тест также будет запущен, если тест является частью категории тестов для запуска.
Использование этого атрибута — запуск только длительных тестов, когда это явно требуется, когда служба запущена и т. Д.
Тайм-аут
Этот атрибут может использоваться, чтобы гарантировать, что тест выполняется только в течение указанного времени в миллисекундах. Если тест превышает указанный период времени, бегун отменяет выполнение и сообщает о том, что тест не пройден.
Сравните этот атрибут с атрибутом MaxTime, описанным ниже.
Также обратите внимание, что при запуске модульных тестов в отладчике атрибут Timeout игнорируется — в противном случае отлаживаемый тест может завершиться, поскольку вы вручную просматриваете код, просматриваете значения и т. Д.
Атрибуты культуры
- культура
- SetCulture
- SetUICulture
культура
Атрибут Культура может использоваться для прибора, чтобы определить культуры, для которых должны выполняться тесты. Тесты, которые специфичны для конкретной культуры, также должны быть украшены этим атрибутом, описывающим тестируемые культуры. Тест или прибор пропускаются, если текущая культура не соответствует культуре, для которой написан тест. Например:
[TestFixture, SetupData] [Культура ("фр, ан")] открытый класс SetupTeardownFlow { [Test, Category ("ObjectBag")] [Культура ( "пт")] публичный void TestA () { Console.WriteLine ("FileX =" + Globals.objectBag ["FileX"]. ToString ()); } [Тест, Категория («Базовый»)] public void TestB () { Console.WriteLine («Тест Б.»); } }
В предыдущем коде прибор работает, если текущая культура имеет значение «fr» или «en»; тем не менее, TestA указывает, что тест должен выполняться только в том случае, если для культуры задано значение «fr.». Следовательно, это устройство приведет к:
поскольку «TestA» не запускается, потому что текущая культура не «фр.»
Такие тесты, как «TestB», в которых не указана какая-либо спецификация культуры, всегда выполняются, если только весь прибор не исключен, поскольку текущая культура не соответствует.
SetCulture
Этот атрибут, применяемый ко всему устройству или определенным тестам, устанавливает текущую Культуру на время теста (или тестов в приборе), а затем восстанавливает исходное значение. В отличие от атрибута Культура до этого, можно указать только одну культуру, хотя в соответствии с документацией NUnit, запуск тестов для нескольких культур запланирован как будущее усовершенствование. Обратите внимание, что настройка культуры в устройстве не влияет на выполнение теста с использованием атрибута Культура. Например:
[TestFixture] [SetCulture ( "FR-FR")] общественный класс CultureTests { [Тестовое задание] [Культура ( "FR-FR")] публичный void TestA () { двойное значение = 1,2; Console.WriteLine (value.ToString ("0,00", CultureInfo.CurrentCulture)); } [Тестовое задание] public void TestB () { двойное значение = 1,2; Console.WriteLine ("Французский:" + value.ToString ("0,00", CultureInfo.CurrentCulture)); } [Test, SetCulture ("en-US")] public void TestC () { двойное значение = 1,2; Console.WriteLine ("Английский-США:" + value.ToString ("0,00", CultureInfo.CurrentCulture)); } }
результаты в TestA не работает. Также обратите внимание на поведение TestB по умолчанию, когда культура установлена в приборе и как она переопределяется в TestC:
SetUICulture
Этот атрибут должен устанавливать культуру для пользовательского интерфейса; однако, это очевидно ничего не делает. Например (не пишите модульный тест, как это, это только для иллюстрации):
[TestFixture] [SetUICulture ( "FR-FR")] общественный класс CultureTests { открытое событие EventHandler ValueChanged; защищенный CultureTestForm ctForm; защищенный двойной вал; общественное двойное значение { get {return val; } устанавливать { val = значение; if (ValueChanged! = null) { ValueChanged (this, EventArgs.Empty); } } } [TestFixtureSetUp] public void SetupFixture () { ctForm = new CultureTestForm (); ctForm.tbFrench.DataBindings.Add (new Binding ("Text", this, "Value")); } [TestFixtureTearDown] public void TeardownFixture () { ctForm.ShowDialog (); } [Тестовое задание] публичный void TestA () { Значение = 1,2; } }
приводит к значению «1.2», отображаемому в моей текущей культуре, будучи en-US:
Если я изменю атрибут на «SetCulture», то пользовательский интерфейс отобразит значение в правильном формате культуры (возможно, вам придется щуриться, чтобы увидеть разницу между «1 балл 2» и «1 запятая 2»:
Параметризованные тесты
Параметризованные тесты — это тесты, в которых модульный тест описывает параметры и их значения, которые будут использоваться для итеративного запуска теста, пока не будут выполнены все комбинации параметров.
Например, этот тест:
[TestFixture] открытый класс ParameterizedTests { [Тестовое задание] public void TestWithParams ([Значения (1, 2, 3)] int x) { Console.WriteLine ("X =" + x); } }
приводит к следующему выводу (и обратите внимание, что график теста также отображает параметры теста):
Следующие атрибуты используются для параметризованного тестирования:
- Ценности
- ValuesSource
- комбинаторный
- Попарно (не реализовано)
- последовательный
- случайный
- Ассортимент
- Прецедент
- TestCaseSource
Ценности
Как показано в предыдущем примере, атрибут Values используется для указания значений, передаваемых в метод теста для каждого параметра. Поскольку это значения, назначенные в атрибуте, они должны иметь тип значения: константные выражения, выражения typeof или массив типов атрибутов.
Также обратите внимание, что NUnit по умолчанию будет проверять каждую комбинацию значений для каждого параметра (см. Атрибуты Combinatorial, Pairwise и Sequential).
ValuesSource
С атрибутом ValueSource вы можете указать источник IEnumerable для значений определенного параметра. Источником может быть поле, свойство или метод отдельного класса или текущего класса. Например, все это допустимые способы выражения источников стоимости:
открытый класс NumeratorList { список защищенных значений; public IEnumerable Values {get {return values;}} public NumeratorList () { значения = новый список () {10, 20, 30}; } } открытый класс DenominatorList { общедоступные значения списка = новый список () {1, 2, 3}; } [TestFixture] открытый класс ValueSourceExamples { Результаты списка = новый список () {10, 10, 10}; [Тест, Последовательный] public void DivideTest ( [ValueSource (typeof (NumeratorList), «Значения»)] int n, [ValueSource (typeof (DenominatorList), "values")] int d, [ValueSource («результаты»)] int Ожидаемый результат) { int r = MyMath.Divide (n, d); Assert.AreEqual (Ожидаемый результат, г); } }
Обратите внимание на использование в тесте атрибута Sequential, который гарантирует, что исходные значения используются последовательно, а не комбинаторно. Поэтому результат:
комбинаторный
Этот атрибут является необязательным, поскольку NUnit автоматически применяет параметры теста во всех возможных комбинациях. Например:
[TestFixture] открытый класс ParameterizedTests { [Тест, комбинаторный] public void TestWithParams ( [Значения (1, 2, 3)] int x, [Значения ("A", "B")] строка s) { Console.WriteLine ("X =" + x + "," + s); } }
приводит ко всем комбинациям «x» и «s», независимо от того, указано «Combinatorial»:
парный
Этот атрибут существует, но не реализован — он предназначен для уменьшения количества комбинаций, когда заданы комбинаторные параметры; однако этот атрибут игнорируется.
последовательный
Этот атрибут указывает, что тесты будут выполняться путем последовательного перехода по значениям параметров. Если количество значений не одинаково для каждого параметра, будет использоваться значение по умолчанию. Обратите внимание, что значение по умолчанию для строки равно нулю. Например, этот код, используя атрибут Sequential:
[TestFixture] открытый класс ParameterizedTests { [Тест, Последовательный] public void TestWithParams ( [Значения (1, 2, 3)] int x, [Значения ("A", "B")] строка s, [Значения (10.1, 10.2, 10.3, 10.4)] double v) { Console.WriteLine ("X =" + x + ", s =" + ((s == null)? "[Null]": s) + ", v =" + v); } }
Результаты в этом выводе:
Обратите внимание, что строка (имеющая только два значения) по умолчанию равна нулю для двух последних параметризованных тестов, а параметр int, имеющий три случая, по умолчанию равен 0 для последнего случая.
случайный
Атрибут Random может применяться с комбинаторными параметризованными тестами для предоставления случайных значений, а не конкретных значений для параметров. Случайные значения параметров всегда двойного типа. Например:
[TestFixture, платформа («Windows7»)] открытый класс ParameterizedTests { [Тестовое задание] public void RandomTest ([Random (5)] double x) { Console.WriteLine ("X =" + x); } }
Результаты в пяти тестах со случайными значениями:
Минимальный и максимальный значения также могут быть указаны для диапазона случайных значений.
С помощью комбинаторных параметризованных тестов легко создать сотни, если не тысячи тестовых случаев. Попробуйте использовать атрибут Sequential, чтобы предотвратить тестирование большого количества комбинаций. Атрибут Sequential, примененный к тесту, может использоваться вместе с атрибутом Random в параметрах теста.
Ассортимент
Атрибут Range указывает диапазон значений для тестирования. За исключением целочисленного диапазона, другие диапазоны (long, float, double) требуют указания шага. Шаг для целочисленных значений не является обязательным.
Диапазоны объединяются с другими параметризованными значениями, поэтому снова рассмотрите возможность использования атрибута Sequential для ограничения числа генерируемых тестовых комбинаций.
Прецедент
Вместо комбинаторного или последовательного параметризованного теста вы можете явно определить значения параметров, передаваемых в каждый тестовый набор для запуска для конкретного теста. Преимущество этого заключается в уменьшении количества комбинаторных тестов, а также в нацеливании на конкретные сценарии тестирования. Кроме того, атрибут TestCase включает в себя дополнительную характеристику возможности проверять возвращаемое значение (при условии, что это тип «значение») со значением, указанным в метаданных атрибута. Наконец, дополнительные свойства, такие как атрибуты зеркального отображения, отображаются в атрибуте TestCase.
Базовый тестовый пример выглядит следующим образом (обратите внимание на отсутствие атрибута TestFixture):
открытый класс TestCaseExamples { public int Divide (int n, int d) { возврат н / д; } [TestCase (10, 2)] [TestCase (20, 10)] public void DivideTest (int n, int d) { int r = Divide (n, d); Assert.AreEqual (r, n / d); } }
Однако реальная сила атрибута TestCase заключается в возможности напрямую применять тестовые примеры к методу и проверять результат:
открытый класс TestCaseExamples { [TestCase (10, 2, Result = 5)] [TestCase (20, 10, Result = 2)] public int Divide (int n, int d) { возврат н / д; } }
Помните, что значения, указанные в атрибутах, должны быть типами значений: константное выражение, выражение typeof или выражение создания массива типа параметра атрибута.
Также имейте в виду, что атрибут TestCase может побуждать помещать тестовые наборы в ту же сборку, что и рабочий код, а не в отдельную тестовую сборку — атрибуты не удаляются в сборке выпуска сборки. При использовании атрибута TestCase рекомендуется не применять атрибут непосредственно к коду, для которого вы хотите тестовые случаи, а скорее к самому тестовому устройству. Так что в идеале правильный код должен выглядеть примерно так:
// В сборке приложения: открытый статический класс MyMath { public static int Divide (int n, int d) { возврат н / д; } } // В модульном тесте сборки: [TestFixture] открытый класс TestCaseExamples { [TestCase (10, 2, Result = 5)] [TestCase (20, 10, Result = 2)] public int DivideTest (int n, int d) { вернуть MyMath.Divide (n, d); } }
в котором класс MyMath находится в сборке приложения, а тестовое устройство — в сборке модульного теста. Затем метод DivideTest становится тонкой оболочкой для метода приложения.
Другие свойства, которые могут быть назначены TestFixture, проиллюстрированы в этом примере:
[TestFixture] открытый класс TestCaseExamples {[TestCase (10, 2, Result = 5, Description = "Нормальное использование.", TestName = "Нормальный тест деления.")] [TestCase (5, 0, ExpectedException = typeof (ArgumentOutOfRangeException), Описание = "Разделить на 0 тестов.", TestName = "Разделить на 0 тестов.")] [TestCase (10, 5, Result = 2, Ignore = true, Reason = "Уже проверено нормальное использование.")] [TestCase ( 15, 5, Result = 3, IgnoreReason = "Еще один, уже проверенный нормальное использование.")] Public int DivideTest (int n, int d) {return MyMath.Divide (n, d); }}
Есть также альтернативы, указывающие ожидаемое исключение:
- ExpectedExceptionName
- ExpectedExceptionMessage
- MatchType
который позволяет вам указать полное имя (соответствующее значению свойства Type.FullName) или сообщение об исключении, а также способ сопоставления сообщения об исключении — Contains, Exact, Regex или StartsWith.
TestCaseSource
Атрибут TestCaseSource ведет себя подобно атрибуту ValueSource, описанному ранее; однако предпочтительной реализацией является указание типа класса, который реализует IEnumerable. Эта предпочтительная реализация ограничивает вас чем-то вроде этого:
открытый класс ValuesList: IEnumerable { общедоступные значения списка = новый список () { new int [] {10, 1, 10}, new int [] {20, 2, 10}, new int [] {30, 3, 10}}; общедоступный IEnumerator GetEnumerator () { вернуть новый ValuesListEnum (values); } } открытый класс ValuesListEnum: IEnumerator { список защищенных значений; защищенный int pos = -1; public ValuesListEnum (Список значений) { this.values = значения; } публичный bool MoveNext () { ++ позы; return pos <values.Count; } Текущий публичный объект { get {return values [pos]; } } public void Reset () { pos = -1; } } [TestFixture] открытый класс TestCaseSourceExamples { [Тест, Последовательный] [TestCaseSource (TypeOf (ValuesList))] public void DivideTest (int n, int d, int Ожидаемый результат) { int r = MyMath.Divide (n, d); Assert.AreEqual (Ожидаемый результат, г); } }
Обратите внимание, что значения тестового примера — это int [] (целочисленный массив), в котором каждый элемент массива отображается на параметр в тестовой функции. Также отметьте еще раз, что атрибут Sequential указан для теста, гарантируя, что данные теста используются последовательно, а не комбинаторно.
Другие атрибуты NUnit
Есть несколько других атрибутов NUnit, описанных далее.
Платформа
Атрибут Platform может использоваться для указания платформы, для которой должен выполняться тест. Если платформа не соответствует текущей платформе, тест не запускается и даже не включается в игнорируемые тесты или общее количество тестов. Просмотрите документацию по адресу http://www.nunit.org/index.php?p=platform&r=2.6.2, чтобы увидеть список поддерживаемых спецификаторов платформы.
Свойство
Вы можете использовать этот атрибут для установки пары имя или значение в тесте или приборе, которые затем отображаются в выходном файле XML. Через отражение вы можете получить доступ к этим атрибутам. Настраиваемые атрибуты свойств также могут быть созданы путем получения настраиваемого класса атрибутов из класса PropertyAttribute.
MaxTime
Атрибут MaxTime (если сравнивать с атрибутом Timeout, описанным ранее) приведет к сбою теста, если он превысит указанное время в миллисекундах. Однако, в отличие от атрибута Timeout, тест будет разрешено завершить.
Повторение
Атрибут Repeat может применяться только к тестам и игнорируется для параметризованных тестов. Он инструктирует тестовый двигатель повторить тест указанное количество раз. Если какое-либо повторение теста не удается, остальные тесты не запускаются, и двигатель сообщает об ошибке.
RequiredAddin
С помощью этого атрибута вы можете указать дополнительные сборки (в файле AssemblyInfo), которые требуются для модульных тестов.
RequiresMTA
Этот атрибут, примененный к тесту или устройству, указывает, что тест должен выполняться в многопоточной квартире. NUnit создаст новый поток для теста, если:
- Сборка еще не выполняется в потоке MTA.
- Содержащий прибор уже не работает в потоке MTA.
Если для всего устройства создан поток, все тесты в этом устройстве выполняются в новом потоке.
RequiresSTA
Этот атрибут, примененный к тесту или устройству, указывает, что тест должен выполняться в однопоточной квартире. NUnit создаст новый поток для теста, если:
- Сборка еще не выполняется в потоке STA.
- Содержащий прибор уже не работает в потоке STA.
Если для всего устройства создан поток, все тесты в этом устройстве выполняются в новом потоке.
RequiresThread
Этот атрибут, примененный к тесту или устройству, указывает, что тест должен выполняться в отдельном потоке. NUnit создаст новый поток, если этот атрибут указан в файле AssemblyInfo:
[Сборка: RequiresThread]
Если в приборе указан этот атрибут, все тесты в приборе запускаются во вновь созданном потоке.
Если в тесте указан этот атрибут, тест будет выполняться во вновь созданном потоке.
Дополнительно можно указать состояние квартиры, что приводит к созданию потока MTA или STA.
Например:
[TestFixture, Требуется Тема] открытый класс ThreadExamples { [Тестовое задание] публичный void TestA () { ЕЫпе (Thread.CurrentThread.ManagedThreadId); } [Test, Требуется Тема] public void TestB () { ЕЫпе (Thread.CurrentThread.ManagedThreadId); } [Тестовое задание] public void TestC () { ЕЫпе (Thread.CurrentThread.ManagedThreadId); } }
Обратите внимание на идентификаторы потоков при выполнении теста:
Тест A и Тест C выполняются в потоке, созданном прибором, тогда как Тест B, указав атрибут RequThread, выполняется в отдельном потоке от потока других тестов в этом приборе.
Теория и Datapoint (s)
Теория — это параметризованный тест, который проверяет допущения о передаваемых значениях. Если допущение не выполняется, тест не проходит. Работает с атрибутом Datapoint (s).
Эти два класса эквивалентны. Первый иллюстрирует использование атрибута Datapoint, второй — атрибут Datapoints:
открытый класс TheoryExample1 { [Datapoint] public int a = 10; [Datapoint] public int b = 5; [Datapoint] public int c = 2; [Datapoint] public int d = 0; [Теория] public void DivideTest (int n, int d, int Ожидаемый результат) { Предположим, что (d> 0); Assume.That (n / d == Ожидаемый результат); int r = MyMath.Divide (n, d); Assert.AreEqual (Ожидаемый результат, г); } } открытый класс TheoryExample2 { [] точек данных public int [] values = new int [] {10, 5, 2, 0}; [Теория] public void DivideTest (int n, int d, int Ожидаемый результат) { Предположим, что (d> 0); Assume.That (n / d == Ожидаемый результат); int r = MyMath.Divide (n, d); Assert.AreEqual (Ожидаемый результат, г); } }
Обратите внимание, что в первом классе, TheoryExample1, используются дискретные значения точек данных, тогда как во втором классе, TheoryExample2, используется массив.
В обоих случаях NUnit создаст все возможные комбинации значений из точек данных. Обратите внимание, как метод теста делает определенные предположения:
- Знаменатель не может быть 0.
- Ожидаемый результат должен быть равен н / д.
Причина второго предположения заключается в том, что тест не пройден для большинства комбинаций — например, 5/10 не равен 2. Как видно из прогона теста, допущения не приводят к провалу теста:
Комбинации значений параметров, которые не соответствуют допущениям, в тесте не учитываются. Типичный образец для теоретического теста:
- Сформулируйте предположения о параметрах.
- Выполните тест.
- Сделайте утверждения о результатах.
Следует соблюдать осторожность при проверке теории из-за растущего числа комбинаций аргументов.
Также обратите внимание, что если явно не указано, NUnit автоматически заполняет комбинации значений параметров для типов bool и enum.
Определенные пользователем атрибуты действий
Атрибуты действий — это пользовательские атрибуты, которые позволяют составлять несвязанный код настройки теста для каждого теста. Зачастую размещение всего кода настройки в настройке прибора или пространства имен нарушает принцип единственной ответственности — некоторые тесты требуют определенных настроек, в то время как другие тесты требуют других настроек. Атрибуты действия позволяют вам точно указать настройку, которая требуется для теста.
Одним из преимуществ пользовательских атрибутов действия является то, что он перемещает код установки и разрыва из самого устройства в более повторно используемую и поддерживаемую базу кода.
Определение действия
Пользовательское действие является производным от TestActionAttribute:
открытый класс UserDefinedAttribute: TestActionAttribute { public override void BeforeTest (TestDetails testDetails) { Console.WriteLine («Пользовательское действие перед тестированием.»); } public override void AfterTest (TestDetails testDetails) { Console.WriteLine («Пользовательское действие после теста.»); } }
или может быть получен из System.Attribute и реализовать интерфейс ITestAction:
открытый класс AnotherUserDefinedAttribute: Attribute, ITestAction { public ActionTargets Targets {get {return ActionTargets.Default; }} public void BeforeTest (TestDetails testDetails) { Console.WriteLine («Другое пользовательское действие перед тестом.»); } public void AfterTest (TestDetails testDetails) { Console.WriteLine («Другое пользовательское действие после теста.»); } }
В последнем случае также должно быть определено свойство «Цели».
Цели действий
Цель действия (либо прибор, либо набор) или метод тестирования можно определить с помощью перечисления ActionTargets (определено в NUnit.Framework):
// Резюме: // Различные цели, к которым может быть применен атрибут тестового действия. [Flags] public enum ActionTargets { // Резюме: // Цель по умолчанию, которая определяется тем, куда прикреплен атрибут действия. По умолчанию = 0, // // Резюме: // Таргетинг на отдельный контрольный пример. Тест = 1, // // Резюме: // Таргетинг на набор тестовых случаев. Люкс = 2, } }
ActionTargets.Default
Указание значения ActionTargets.Default позволяет применять определенный пользователем атрибут как к тестовым осветителям (классам), так и к тестам (методам). Однако при применении к осветителю (классу) атрибут ведет себя так, как если бы был указан ActionTargets.Suite — определяемое пользователем действие выполняется один раз для всего набора тестов.
ActionTargets.Test
Если ActionTargets.Test возвращается пользовательским действием, это меняет поведение при вызове пользовательского действия. Применительно к устройству (классу) пользовательское действие выполняется для каждого теста. Если пользовательский атрибут применяется к определенному тесту, то для этого конкретного теста выполняется пользовательское действие.
ActionTargets.Suite
Если цель действия атрибута является набором, но используется в контексте метода тестирования, об ошибке не сообщается — действие просто не выполняется.
Тест и целевые наборы действий
ActionTargets являются флагами, поэтому их можно комбинировать:
общедоступные ActionTargets Targets { получить { вернуть ActionTargets.Suite | ActionTargets.Test; } }
указать, что пользовательское действие должно выполняться как для прибора (набор тестов), так и для отдельных тестов.
Класс TestDetails
Этот класс создается с информацией о готовящемся приборе или тесте (из NUnit.Framework.TestDetails):
// Резюме: // Приспособление, членом которого является тест, если доступно. публичный объект Fixture {get; } // // Резюме: // Полное имя теста. открытая строка FullName {get; } // // Резюме: // Указывает, представляет ли тест набор тестов. public bool IsSuite {get; } // // Резюме: // Метод, который реализует тест, если доступен. public MethodInfo Method {get; } // // Резюме: // Строка, представляющая тип теста, например, «Test Case». публичная строка Type {get; }
Эти свойства могут быть проверены, и могут быть предприняты определенные действия, такие как предоставление дополнительных диагностических сообщений консоли.
Действия Ассамблеи
Придерживаясь большего в концепции Visual Studio для AssemblyInitialize и AssemblyCleanup, вы можете использовать пользовательское действие на основе сборки. Например, с предыдущим кодом, если в файл AssemblyInfo.cs добавлена следующая строка:
[сборка: UnitTestExamplesNUnit.UserDefined]
тогда пользовательское действие будет выполняться при загрузке сборки:
Пользовательское действие перед тестом. Перед всеми испытаниями светильников. Настройка прибора. ***** UnitTestExamplesNUnit.SetupTeardownFlow.TestA Испытательная установка. Тест А. Тест Разрушение. ***** UnitTestExamplesNUnit.SetupTeardownFlow.TestB Испытательная установка. Тест Б. Тест Разрушение. Fixture Teardown. После всех испытаний светильников. Пользовательское действие после теста.
Если целевым действием для пользовательского действия является Suite, пользовательское действие выполняется до и после всех тестов осветителей, ведя себя как «до или после всех наборов». Однако обратите внимание, что если указан TargetActions.Test, то действие выполняется до или после всех тестов во всех пакетах. Поэтому следует соблюдать осторожность в отношении возвращаемого значения свойства Target и желаемого поведения определенного пользователем действия.
Передача информации в / из тестов из пользовательских действий
Два вопроса могут быть:
- Как передать информацию из прибора в пользовательское действие?
- Как использовать объект, экземпляр которого определяется пользовательским действием в самом тесте?
Передача информации в пользовательское действие
Это обрабатывается прямо как параметры для действий пользователя. Допустим, вы хотите передать строку подключения, чтобы проверить поведение для определенной роли пользователя. Например, используя эту заглушку:
открытый класс InitializeDatabaseConnection: TestActionAttribute { защищенное соединение строк; public InitializeDatabaseConnection (строка myConnection) { соединение = myConnection; } public override void BeforeTest (TestDetails testDetails) { Console.WriteLine («Использование» + соединение); } public override void AfterTest (TestDetails testDetails) { Console.WriteLine («Закрытие соединения.»); } }
и применяя его к светильнику:
[TestFixture, InitializeDatabaseConnection («Имя пользователя = Foo, Пароль = Бар»)] открытый класс SetupTeardownFlow { ... и т.д ...
приводит к следующему потоку испытаний:
Перед всеми испытаниями светильников. Настройка прибора. Используя Имя пользователя = Foo, Пароль = Бар ***** UnitTestExamplesNUnit.SetupTeardownFlow.TestA Испытательная установка. Тест А. Тест Разрушение. ***** UnitTestExamplesNUnit.SetupTeardownFlow.TestB Испытательная установка. Тест Б. Тест Разрушение. Закрытие соединения. Fixture Teardown. После всех испытаний светильников.
Обратите внимание, что пользовательское действие для набора выполняется после настройки «пространства имен» и после настройки прибора. Если для определенного пользователем действия требуется некоторая информация, которую нельзя передать с помощью конструктора (конструкторы атрибутов ограничены константами), то может оказаться полезным подход, аналогичный описанному ниже. Однако имейте в виду, что определяемые пользователем действия должны быть как можно более автономными — следует избегать перепутывания с тестовыми состояниями.
Передача информации из пользовательского действия в Test / Test Suite
Это невозможно без какой-либо формы пограничного контейнера. Чрезвычайно простая реализация — создать пакет объектов из словаря значения ключа:
/// /// Класс-оболочка для поиска значения ключа. /// открытый класс ObjectBag: словарь { } /// /// Глобальный контейнерный класс. /// открытый статический класс Globals { public static ObjectBag objectBag = new ObjectBag (); }
что позволяет отключенным объектам легко обмениваться данными, например, в пользовательском атрибуте действия:
открытый класс SetupData: TestActionAttribute { public override void BeforeTest (TestDetails testDetails) { Globals.objectBag ["FileX"] = "Некоторые данные"; } }
Данные могут быть доступны в тестовых методах:
[Тестовое задание] публичный void TestA () { Console.WriteLine ("FileX =" + Globals.objectBag ["FileX"]. ToString ()); }
Существуют более сложные подходы, и предыдущие примеры предназначены для иллюстрации того, как координировать пользовательское действие с обменом информацией. В предыдущем примере есть множество проблем — безопасность типов, отсутствие проверок того, существует ли объект в контейнере и т. Д. — проблемы, которые должен обрабатывать надежный класс контейнера.
NUnit Основные утверждения
NUnit предоставляет те же или похожие утверждения в классе NUnit.Framework.Assert, что и в классе Assert в Visual Studio, как описано ранее. NUnit также предоставляет дополнительные утверждения:
IsEmpty / IsNotEmpty
Эти утверждения проверяют, является ли строка пустой или нет, или что коллекция пуста или нет:
[TestFixture] публичный класс OtherAssertions { [Тестовое задание] public void IsEmptyTest () { List <object> itemList = new List <object> (); Assert.IsEmpty (ITEMLIST); } }
Больше / Меньше
Эти два утверждения работают с числовыми значениями и объектами, реализующими IComparable, утверждая, что первое значение больше или меньше второго значения. Цель этого утверждения — улучшить читаемость теста.
GreaterOrEqual / LessOrEqual
Эти два утверждения работают с числовыми значениями и объектами, реализующими IComparable, утверждая, что первое значение «больше или равно» или «меньше или равно» второму значению. Цель этого утверждения — улучшить читаемость теста.
IsAssignableFrom / IsNotAssignableFrom
Эта пара методов утверждает, что ожидаемый тип можно назначить из экземпляра объекта совместимого типа. Это похоже на утверждение IsInstanceOfType в тестовом движке Visual Studio. Например, учитывая:
открытый класс A {} открытый класс B: A {} [TestFixture] публичный класс OtherAssertions { [Тестовое задание] public void IsAssignableFrom () { Assert.IsAssignableFrom (typeof (B), new A ()); } }
Этот тест проходит, потому что объекту типа A может быть присвоен экземпляр типа B.
Выдает / Выдает <Т> / DoesNotThrow
Неуниверсальная форма этого утверждения подтверждает, что исключение выдается (или нет) при вызове указанного метода. Например:
[Тестовое задание] public void DivideByZeroThrownTest () { Assert.Throws (typeof (ArgumentOutOfRangeException), () => MyMath.Divide (5, 0)); }
Этот тест проходит.
Универсальная версия помещает тип исключения в качестве универсального параметра:
[Тестовое задание] public void DivideByZeroThrownTest () { Assert.Throws (() => MyMath.Divide (5, 0)); }
Стопор / улов <T>
Эти два метода утверждают, что метод генерирует тип исключения или один из его производных. Напротив, метод Throws до этого утверждает, что выбрасывается именно указанный тип исключения. Например, этот код передает:
[Тестовое задание] публичный void CatchTest () { Assert.Catch (() => MyMath.Divide (5, 0)); }
Сборник утверждений
В классе NUnit.Framework.CollectionAssert NUnit реализует те же утверждения, что и в классе CollectionAssert Visual Studio, с этими дополнительными утверждениями:
- IsEmpty / IsNotEmpty
- IsOrdered
Обратите внимание, что параметр коллекции в этих методах предполагает, что коллекция будет реализовывать IEnumerable (в отличие от тестовой среды Visual Studio, которая ожидает ICollection).
IsEmpty / IsNotEmpty
Метод CollectionAssert.IsEmpty такой же, как метод Assert.IsEmpty.
IsOrdered
Этот метод проверяет, что значения в коллекции упорядочены. Например, потому что AnObject реализует IComparable:
открытый класс AnObject: IComparable { public int SomeValue {get; устанавливать; } public override bool Equals (объектный объект) { return SomeValue == ((AnObject) obj) .SomeValue; } public int CompareTo (объектный объект) { return SomeValue.CompareTo (((AnObject) obj) .SomeValue); } }
этот тест проходит:
[Тестовое задание] public void OrderedObjectTest () { Список <объект> itemList = новый Список <объект> () { новый AnObject () {SomeValue = 1}, новый AnObject () {SomeValue = 2} }; CollectionAssert.IsOrdered (ITEMLIST); }
Строковые утверждения
Эти методы утверждения являются членами класса NUnit.Framework.StringAssert и такие же, как и в классе StringAssert в Visual Studio, с несколькими отличиями:
- AreEqualIgnoringCase
- IsMatch
которые обсуждаются далее.
AreEqualIgnoringCase
Утверждение «AreEqualIgnoringCase» выполняет сравнение двух строк, игнорируя регистр.
IsMatch
Этот метод похож на метод StringAssert.Matches в Visual Studio — он утверждает, что строка соответствует шаблону регулярного выражения.
Утверждения файла
Эти методы утверждения являются членами класса NUnit.Framework.FileAssert.
AreEqual / AreNotEqual
Эти методы утверждают, что два файла идентичны или нет. Побайтовое сравнение выполняется для двух файлов. Если файлы пусты, они считаются равными. Если один или оба файла не существуют, выдается исключение FileNotFoundException.
Утверждения каталога
Эти методы утверждения являются членами класса NUnit.Framework.DirectoryAssert.
AreEqual / AreNotEqual
Эти методы утверждают, что файлы в двух каталогах одинаковы или нет.
IsEmpty / IsNotEmpty
Эти методы утверждают, что каталог пуст или нет.
IsWithin / IsNotWithin
Эти методы утверждают, что путь содержит указанный подкаталог или нет.
Другие утверждения
Тот
В то время как одна из форм этого метода принимает булевский параметр в качестве подтверждения истины (ведет себя точно так же, как Assert.IsTrue), преобладающей формой этого метода является возможность сравнивать фактическое значение с использованием определенного ограничения. Документация NUnit по ограничениям довольно обширна в отношении ограничений, поэтому здесь будет приведен только краткий пример. Например, учитывая этот тест:
[TestFixture] открытый класс CollectionTests { [Тестовое задание] public void AreEquivalentTest () { Список <объект> itemList1 = новый список <объект> () { новый AnObject () {SomeValue = 1}, новый AnObject () {SomeValue = 2} }; Список <объект> itemList2 = новый список <объект> () { новый AnObject () {SomeValue = 2}, новый AnObject () {SomeValue = 1} }; CollectionAssert.AreEquivalent (itemList1, itemList2); } }
Используя систему ограничений, утверждение может быть переписано как:
[TestFixture] открытый класс CollectionTests { [Тестовое задание] public void AreEquivalentTest () { Список <объект> itemList1 = новый список <объект> () { новый AnObject () {SomeValue = 1}, новый AnObject () {SomeValue = 2} }; Список <объект> itemList2 = новый список <объект> () { новый AnObject () {SomeValue = 2}, новый AnObject () {SomeValue = 1} }; Assert.That (itemList1, Is.EquivalentTo (itemList2); } }
Синтаксис ограничений способствует удобочитаемости модульного теста, а также расширяемой архитектуре для новых методов.
IsNaN
Этот метод проверяет, является ли двойное значение «не числом». Типичным случаем, когда значение не является числом, является деление на ноль или квадратный корень из отрицательного значения.
Полезные методы
Класс assert также предоставляет несколько служебных методов:
Проходят
Assert.Pass немедленно завершает тест, выдавая исключение SuccessException, в результате чего тест помечается как проходящий.
Провал
Assert.Fail немедленно завершает тест, выдавая исключение AssertionException, в результате чего тест помечается как неудачный.
игнорировать
Assert.Ignore немедленно завершает тест, выдавая IgnoreException, сообщая о том, что тест игнорируется.
неубедительный
Assert.Inconclusive немедленно завершает тест, выдавая исключение InconclusiveException, сообщая о том, что тест не завершен. Это может быть полезно, когда модульное тестирование или устройство определяет, что требуемая служба не работает, и поэтому тесты для этой службы не могут быть продолжены.