Управление порядком выполнения модульных тестов в Visual Studio
Хорошо, я закончил поиск хорошей информации об этом.
У меня есть серия тестов единиц, которые вызывают статический класс, который после инициализации устанавливает свойства, которые не могут (или я не хочу) изменять.
Моя проблема заключается в том, что я не могу выполнить установленный порядок для запуска тестов. Если бы я мог, я мог бы запускать их таким образом, чтобы статические свойства были установлены надежным способом, и я мог бы утверждать их, но, к сожалению, инфраструктура Microsoft.VisualStudio.TestTools.UnitTesting запускает их только в случайном порядке.
Итак, я нашел этот http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.priorityattribute.aspx, который говорит в разделе "Замечания": "Этот атрибут не используется тестовой системой. Он предоставляется пользователю для пользовательских цели". А? Что хорошего тогда? Они ожидают, что я напишу свою собственную тестовую оболочку, чтобы воспользоваться этим невероятным атрибутом (из которого я мог бы легко написать себя, если бы захотел пойти на этот уровень усилий...)
Итак, хватит напыщенности; Итог, есть ли способ контролировать порядок выполнения моих модульных тестов?
[TestMethod]
[Priority(0)]
и т.д.. НЕ работает, что имеет смысл, так как Microsoft говорит, что этого не будет.
Кроме того, не прошу прокомментировать "нарушение изоляции". TestClass изолирует то, что я тестирую, а не отдельные TestMethods. Независимо от того, каждый тест может выполняться независимо, просто отлично, они просто не могут быть объединены в случайном порядке, так как нет возможности снести статический класс.
О, я также знаю о "Упорядоченном тесте".
Ответы
Ответ 1
Объедините свои тесты в один гигантский тест. Чтобы сделать тестовый метод более читаемым, вы можете сделать что-то вроде
[TestMethod]
public void MyIntegratonTestLikeUnitTest()
{
AssertScenarioA();
AssertScenarioB();
....
}
private void AssertScenarioA()
{
// Assert
}
private void AssertScenarioB()
{
// Assert
}
Фактически проблема, о которой вы говорите, предполагает, что вы, вероятно, должны улучшить проверяемость реализации.
Ответ 2
Вы можете использовать Список воспроизведения
Щелкните правой кнопкой мыши метод тестирования → Добавить в список воспроизведения → Новый плейлист
порядок выполнения будет таким, как вы добавляете их в список воспроизведения, но если вы хотите его изменить, у вас есть файл
![enter image description here]()
Ответ 3
Я не вижу никого, кто бы упоминал ClassInitialize
атрибута ClassInitialize
. Атрибуты довольно просты.
Создайте методы, помеченные [ClassInitialize()]
или [TestInitialize()]
чтобы подготовить аспекты среды, в которой будет выполняться ваш unit тест. Цель этого - установить известное состояние для запуска вашего модульного теста. Например, вы можете использовать метод [ClassInitialize()]
или [TestInitialize()]
для копирования, изменения или создания определенных файлов данных, которые будет использовать ваш тест.
Создайте методы, помеченные [ClassCleanup()]
или [TestCleanUp{}]
чтобы вернуть среду в известное состояние после выполнения теста. Это может означать удаление файлов в папках или возвращение базы данных в известное состояние. Примером этого является сброс базы данных инвентаризации в исходное состояние после тестирования метода, который используется в приложении для ввода заказа.
-
[ClassInitialize()]
Используйте ClassInitialize
для запуска кода перед запуском первого теста в классе.
-
[ClassCleanUp()]
Используйте ClassCleanup
для запуска кода после выполнения всех тестов в классе.
-
[TestInitialize()]
Используйте TestInitialize
для запуска кода перед запуском каждого теста.
-
[TestCleanUp()]
Используйте TestCleanup
для запуска кода после каждого теста.
Ответ 4
Как уже отмечали комментаторы, тесты, зависящие от других тестов, указывают на недостаток дизайна. Тем не менее есть способы добиться этого. Как было сказано в ранее заданном вопросе here, вы можете создать упорядоченные модульные тесты, которые в основном представляют собой один тестовый контейнер, который обеспечивает проверку последовательность.
Вот руководство по MSDN: http://msdn.microsoft.com/en-us/library/ms182631.aspx
Ответ 5
Поскольку вы уже упоминали о функциях Ordered Test, которые поставляются в инфраструктуре тестирования Visual Studio, я проигнорирую это. Вы также, похоже, знаете, что то, что вы пытаетесь выполнить, чтобы проверить этот статический класс, является "плохой идеей", поэтому я проигнорирую это.
Вместо этого давайте сосредоточимся на том, как вы действительно сможете гарантировать, что ваши тесты будут выполнены в том порядке, в котором вы хотите. Один из вариантов (как указано в @gaog) - это "один тестовый метод, множество тестовых функций", вызывающий ваши тестовые функции в том порядке, в котором вы хотите, в пределах одной функции, помеченной атрибутом TestMethod
. Это самый простой способ, и единственным недостатком является то, что первая тестовая функция сбой предотвратит выполнение любой из оставшихся тестовых функций.
С описанием ситуации, это решение, которое я предлагаю вам использовать.
Если вам выделена полужирная часть, вы можете выполнить упорядоченное выполнение изолированных тестов, используя встроенные функции тестирования, основанные на данных. Его сложнее и немного грязно, но он выполняет свою работу.
Короче говоря, вы определяете источник данных (например, файл CSV или таблицу базы данных), который контролирует порядок, в котором вам нужно запускать тесты, и имена функций, которые фактически содержат тестовые функции. Затем вы подключаете этот источник данных к тесту, управляемому данными, используйте параметр последовательного чтения и выполняете свои функции в том порядке, в котором вы хотите, в качестве отдельных тестов.
[TestClass]
public class OrderedTests
{
public TestContext TestContext { get; set; }
private const string _OrderedTestFilename = "TestList.csv";
[TestMethod]
[DeploymentItem(_OrderedTestFilename)]
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", _OrderedTestFilename, _OrderedTestFilename, DataAccessMethod.Sequential)]
public void OrderedTests()
{
var methodName = (string)TestContext.DataRow[0];
var method = GetType().GetMethod(methodName);
method.Invoke(this, new object[] { });
}
public void Method_01()
{
Assert.IsTrue(true);
}
public void Method_02()
{
Assert.IsTrue(false);
}
public void Method_03()
{
Assert.IsTrue(true);
}
}
В моем примере у меня есть поддерживающий файл TestList.csv, который копируется для вывода. Это выглядит так:
TestName
Method_01
Method_02
Method_03
Ваши тесты будут выполняться в указанном вами порядке и при обычной тестовой изоляции (т.е. если один из них не сработает, остальные будут выполняться, но будут использоваться статические классы).
Вышесказанное на самом деле является лишь основной идеей, если я буду использовать ее в производстве, я бы сгенерировал имена тестовых функций и их порядок динамически до запуска теста. Возможно, используя PriorityAttribute, который вы нашли, и некоторый простой код отражения для извлечения методов тестирования в классе и упорядочения их соответствующим образом, затем напишите этот порядок в источник данных.
Ответ 6
Вот класс, который можно использовать для настройки и запуска упорядоченных тестов независимо от основы MS Ordered Tests по любой причине - например, не нужно настраивать аргументы mstest.exe на машине сборки или смешивать упорядоченные с неупорядоченными в класс.
Исходная среда тестирования видит только список упорядоченных тестов в качестве одного теста, поэтому любой init/cleanup, такой как [TestInitalize()] Init(), вызывается только до и после всего набора.
Применение:
[TestMethod] // place only on the list--not the individuals
public void OrderedStepsTest()
{
OrderedTest.Run(TestContext, new List<OrderedTest>
{
new OrderedTest ( T10_Reset_Database, false ),
new OrderedTest ( T20_LoginUser1, false ),
new OrderedTest ( T30_DoLoginUser1Task1, true ), // continue on failure
new OrderedTest ( T40_DoLoginUser1Task2, true ), // continue on failure
// ...
});
}
Реализация:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace UnitTests.Utility
{
/// <summary>
/// Define and Run a list of ordered tests.
/// 2016/08/25: Posted to SO by crokusek
/// </summary>
public class OrderedTest
{
/// <summary>Test Method to run</summary>
public Action TestMethod { get; private set; }
/// <summary>Flag indicating whether testing should continue with the next test if the current one fails</summary>
public bool ContinueOnFailure { get; private set; }
/// <summary>Any Exception thrown by the test</summary>
public Exception ExceptionResult;
/// <summary>
/// Constructor
/// </summary>
/// <param name="testMethod"></param>
/// <param name="continueOnFailure">True to continue with the next test if this test fails</param>
public OrderedTest(Action testMethod, bool continueOnFailure = false)
{
TestMethod = testMethod;
ContinueOnFailure = continueOnFailure;
}
/// <summary>
/// Run the test saving any exception within ExceptionResult
/// Throw to the caller only if ContinueOnFailure == false
/// </summary>
/// <param name="testContextOpt"></param>
public void Run()
{
try
{
TestMethod();
}
catch (Exception ex)
{
ExceptionResult = ex;
throw;
}
}
/// <summary>
/// Run a list of OrderedTest's
/// </summary>
static public void Run(TestContext testContext, List<OrderedTest> tests)
{
Stopwatch overallStopWatch = new Stopwatch();
overallStopWatch.Start();
List<Exception> exceptions = new List<Exception>();
int testsAttempted = 0;
for (int i = 0; i < tests.Count; i++)
{
OrderedTest test = tests[i];
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
testContext.WriteLine("Starting ordered test step ({0} of {1}) '{2}' at {3}...\n",
i + 1,
tests.Count,
test.TestMethod.Method,
DateTime.Now.ToString("G"));
try
{
testsAttempted++;
test.Run();
}
catch
{
if (!test.ContinueOnFailure)
break;
}
finally
{
Exception testEx = test.ExceptionResult;
if (testEx != null) // capture any "continue on fail" exception
exceptions.Add(testEx);
testContext.WriteLine("\n{0} ordered test step {1} of {2} '{3}' in {4} at {5}{6}\n",
testEx != null ? "Error: Failed" : "Successfully completed",
i + 1,
tests.Count,
test.TestMethod.Method,
stopWatch.ElapsedMilliseconds > 1000
? (stopWatch.ElapsedMilliseconds * .001) + "s"
: stopWatch.ElapsedMilliseconds + "ms",
DateTime.Now.ToString("G"),
testEx != null
? "\nException: " + testEx.Message +
"\nStackTrace: " + testEx.StackTrace +
"\nContinueOnFailure: " + test.ContinueOnFailure
: "");
}
}
testContext.WriteLine("Completed running {0} of {1} ordered tests with a total of {2} error(s) at {3} in {4}",
testsAttempted,
tests.Count,
exceptions.Count,
DateTime.Now.ToString("G"),
overallStopWatch.ElapsedMilliseconds > 1000
? (overallStopWatch.ElapsedMilliseconds * .001) + "s"
: overallStopWatch.ElapsedMilliseconds + "ms");
if (exceptions.Any())
{
// Test Explorer prints better msgs with this hierarchy rather than using 1 AggregateException().
throw new Exception(String.Join("; ", exceptions.Select(e => e.Message), new AggregateException(exceptions)));
}
}
}
}
Ответ 7
Как вы должны знать, пуристы говорят, что запрещают запускать заказываемые тесты. Это может быть справедливо для модульных тестов. MSTest и другие Unit Test фреймворки используются для запуска чистых Unit Test а также тестов пользовательского интерфейса, полных тестов интеграции, которые вы называете. Может быть, нам не следует называть их Unit Test фреймворками, или, может быть, мы должны использовать их в соответствии с нашими потребностями. Это то, что делают большинство людей.
Я запускаю VS2015, и я ДОЛЖЕН запускать тесты в определенном порядке, потому что я запускаю тесты пользовательского интерфейса (Selenium).
Приоритет - ничего не делает
Этот атрибут не используется тестовой системой. Он предоставляется пользователю для специальных целей.
orderedtest - он работает, но я не рекомендую его, потому что:
- Приказал упорядочить текстовый файл, в котором перечислены ваши тесты, в том порядке, в котором они должны быть
казнены. Если вы измените имя метода, вы должны исправить файл.
- Порядок выполнения проверки соблюдается внутри класса. Вы не можете заказать
какой класс выполняет свои тесты в первую очередь.
- Упорядоченный файл привязан к конфигурации: Debug или Release
- У вас может быть несколько файлов ordertest, но данный метод не может быть повторен в разных файлах упорядоченного набора. Таким образом, у вас не может быть одного файла упорядоченного файла для Debug и другого для Release.
Другие предложения в этой теме интересны, но вы теряете способность следить за прогрессом в Test Explorer.
Вы останетесь с решением, которое попросит пурист, но на самом деле это решение, которое работает: сортировать по заказу.
Исполнитель MSTest использует interop, которому удается получить заказ объявления, и этот трюк будет работать до тех пор, пока Microsoft не изменит код тестового исполнителя.
Это означает, что тестовый метод, объявленный в первую очередь, выполняется до того, который объявлен на втором месте и т.д.
Чтобы облегчить вашу жизнь, заказ объявления должен соответствовать алфавитному порядку, который показан в проводнике тестеров.
- A010_FirstTest
- A020_SecondTest
- и т.д.
- A100_TenthTest
Я настоятельно рекомендую некоторые старые и проверенные правила:
- используйте шаг 10, потому что вам нужно будет вставить тестовый метод позже
- избегайте нумерации ваших тестов, используя щедрый шаг между номерами тестов
- используйте 3 цифры для номера ваших тестов, если вы используете более 10 тестов.
- используйте 4 цифры для номера ваших тестов, если вы используете более 100 тестов.
ОЧЕНЬ ВАЖНО
Чтобы выполнить тесты по порядку декларации, вы должны использовать Запустить все в Проводнике тестеров.
Скажем, у вас есть 3 тестовых класса (в моих тестах для Chrome, Firefox и Edge). Если вы выберете данный класс и щелкните правой кнопкой мыши Запустить выбранные тесты, он обычно начинается с выполнения метода, объявленного на последнем месте.
Опять же, как я уже говорил, заявленный порядок и указанный порядок должны совпадать, иначе вы будете в большой беде в кратчайшие сроки.
Ответ 8
Я не буду описывать порядок тестов, извините. Другие уже это сделали. Кроме того, если вы знаете о "заказанных тестах" - ну, это ответ MS VS на проблему. Я знаю, что эти заказы-тесты не забавны. Но они думали, что это будет "это", и в MSTest в этом нет ничего более.
Я пишу об одном из ваших предположений:
поскольку нет способа снести статический класс.
Если ваш статический класс не представляет собой внешнее внешнее состояние всей вашей внешней среды (например, состояние неуправляемой библиотеки библиотек DLL, которое P/вызывается остальной частью вашего кода), ваш что there is no way
неверно.
Если ваш статический класс относится к этому, то извините, вы совершенно правы, остальная часть этого anwer не имеет значения. Тем не менее, как вы не сказали этого, я предполагаю, что ваш код "управляется".
Подумайте и проверьте AppDomain
штуку. Редко это необходимо, но это именно тот случай, когда вы, вероятно, хотели бы их использовать.
Вы можете создать новый AppDomain и создать там экземпляр теста и запустить там тестовый метод. Статические данные, используемые управляемым кодом, будут изолированы там, и по завершении вы сможете выгрузить AppDomain, и все данные, включенные в статику, будут испаряться. Затем следующий тест будет инициализировать другой appdomain и т.д.
Это будет работать, если у вас нет внешнего состояния, которое вы должны отслеживать. AppDomains изолирует только управляемую память. Любая родная DLL будет по-прежнему загружаться за процесс, и их состояние будет доступно всем AppDomains.
Кроме того, создание/разрывание областей приложения будет, тем не менее, замедлить тесты. Кроме того, у вас могут возникнуть проблемы с разрешением сборки в дочернем домене appdomain, но они разрешимы с разумным количеством кода многократного использования.
Кроме того, у вас могут возникнуть небольшие проблемы с передачей тестовых данных - и обратно - дочерним AppDomain. Прошедшие объекты должны либо быть сериализуемыми в некотором роде, либо быть MarshalByRef
или т.д. Говорящий кросс-домен почти похож на IPC.
Однако, позаботьтесь, это будет 100% управляемый разговор. Если вы немного позаботитесь и добавите небольшую работу в настройку AppDomain, вы сможете даже передавать делегаты и запускать их в целевом домене. Затем, вместо того, чтобы сделать несколько волосатых кросс-доменных настроек, вы можете скомпоновать свои тесты на что-то вроде:
void testmethod()
{
TestAppDomainHelper.Run( () =>
{
// your test code
});
}
или даже
[IsolatedAppDomain]
void testmethod()
{
// your test code
}
если ваша тестовая среда поддерживает создание таких оболочек/расширений. После некоторых начальных исследований и работы их использование почти тривиально.
Ответ 9
я нашел этот атрибут [TestCategory("1")]
.
меня устраивает. Я хотел бы поделиться для вас.
Примечание: MSTest
Ответ 10
Я вижу, что этой теме уже почти 6 лет, и теперь у нас есть новая версия Visual Studio, но я все равно отвечу. У меня была проблема с порядком в Visual Studio 19, и я решил ее, добавив заглавную букву (вы также можете добавить маленькую букву) перед именем вашего метода и в алфавитном порядке, например:
[TestMethod]
public void AName1()
{}
[TestMethod]
public void BName2()
{}
И так далее. Я знаю, что это не выглядит привлекательным, но похоже, что Visual сортирует ваши тесты в тестовом проводнике в алфавитном порядке, независимо от того, как вы пишете это в своем коде. Плейлист не работает для меня в этом случае.
Надеюсь, что это поможет.
Ответ 11
они просто не могут запускаться вместе в случайном порядке, так как нет возможности снести статический класс
Вы можете назвать пространства имен и классы в алфавитном порядке. например:.
- MyApp.Test. Stage01 _setup. Step01 _BuildDB
- MyApp.Test. Stage01 _setup. Step02 _UpgradeDB
- MyApp.Test. Stage02 _Domain. Step01 _TestMyStaff
- MyApp.Test. Stage03 _Integration. Step01 _TestMyStaff
где MyApp.Test.Stage01_Setup
- пространство имен, а Step01_BuildDB
- имя класса.