Подтвердить что ты не робот

Управление порядком выполнения модульных тестов в 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. Независимо от того, каждый тест может выполняться независимо, просто отлично, они просто не могут быть объединены в случайном порядке, так как нет возможности снести статический класс.

О, я также знаю о "Упорядоченном тесте".

4b9b3361

Ответ 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 - имя класса.