Могу ли я создать настраиваемый таймер TestContext для UnitTest/LoadTest в Visual Studio?

Некоторые из моих UnitTests имеют Сон, определенный в цикле. Я хочу профилировать не только каждую итерацию теста, но и общее время для всех итераций, чтобы показать любое нелинейное масштабирование. Например, если я профилю "В целом", он включает время для сна. Я могу использовать Stopwatch Start/Stop, чтобы он включал только doAction(). Тем не менее, я не могу записать результаты Секундомера в результаты TestContext.

[TestMethod]
    public void TestMethod1()
    {
        TestContext.BeginTimer("Overall");
        for (int i = 0; i < 5; i++)
        {
            TestContext.BeginTimer("Per");
            doAction();
            TestContext.EndTimer("Per");
            Sleep(1000);
        }
        TestContext.EndTimer("Overall");
    }

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

Есть ли реализация для этого, на которую я могу ссылаться, или другую идею. Я хотел бы увидеть это в том же отчете, что Visual Studio представляет для LoadTest. В противном случае я должен написать свои собственные отчеты.

Кроме того, я попытался обнюхать SQL, который записывает их в базу данных LoadTest, но не удалось выяснить, как это сделать. Для вызова должен быть SPROC, но я думаю, что это все данные в конце теста.

Ответ 1

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

Во-первых, это невозможно сделать так, как вы пытаетесь. Прямой связи между испытанием нагрузки и Unit Test, где существует TestContext, нет прямой связи.

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

Как редактировать счетчики

Конфигурация тестового теста нагрузки имеет два основных раздела относительно счетчиков. Это:

  • Counter Sets. Это набор счетчиков, например agent, который добавляется по умолчанию. Если вы откроете этот счетчик, вы увидите, что он собирает счетчики, такие как Memory, Processor, PhysicalDisk e.t.c. Итак, в конце теста вы можете увидеть все эти данные у всех ваших агентов. Если вы хотите добавить больше счетчиков в этот счетчик, вы можете дважды щелкнуть по нему (из редактора тестовых нагрузок, см. Рисунок ниже) и выбрать Add Counters. Это откроет окно со всеми счетчиками вашей системы и выберите те, которые вы хотите.

  • Counter Set Mappings. Здесь вы связываете наборы счетчиков с вашими машинами. По умолчанию [CONTROLLER MACHINE] и [AGENT MACHINES] добавляются некоторые наборы счетчиков по умолчанию. Это означает, что все счетчики, содержащиеся в наборах счетчиков, которые отображаются на [CONTROLLER MACHINE], будут собраны с вашего компьютера-контроллера. То же самое относится ко всем вашим агентам.

enter image description here

Вы можете добавить больше наборов счетчиков и больше машин. Щелкнув правой кнопкой мыши по Counter Set MappingsManage Counter Sets..., откроется новое окно:

enter image description here

Как вы можете видеть, я добавил дополнительную машину с именем db_1. Это компьютерное имя машины, и он должен находиться в том же домене с контроллером, чтобы иметь доступ к нему и собирать счетчики. Я также отметил его как database server и выбрал набор счетчиков sql (по умолчанию для счетчиков sql, но вы можете отредактировать его и добавить любой счетчик, который вы хотите). Теперь каждый раз, когда выполняется это испытание нагрузки, контроллер переходит к машине с именем компьютера db_1 и собирает данные, которые будут представлены в окончательных результатах теста.


Теперь кодирующая часть

Хорошо, после этого (большого) введения вам пора посмотреть, как добавить свои данные в итоговые результаты теста. Для этого вы должны создать собственный пользовательский счетчик производительности. Это означает, что в машинах, необходимых для сбора этих данных, должна быть создана новая Категория счетчика производительности. В вашем случае, во всех ваших агентах, потому что здесь выполняется UnitTests.

После того, как вы создали счетчики в агентах, вы можете отредактировать счетчик Agents, как показано выше, и выбрать дополнительные пользовательские счетчики.

Вот пример кода о том, как это сделать.

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

void CreateCounter() 
{
    if (PerformanceCounterCategory.Exists("MyCounters"))
    {
        PerformanceCounterCategory.Delete("MyCounters");
    }

    //Create the Counters collection and add your custom counters 
    CounterCreationDataCollection counters = new CounterCreationDataCollection();
    // The name of the counter is Delay
    counters.Add(new CounterCreationData("Delay", "Keeps the actual delay", PerformanceCounterType.AverageCount64));
    // .... Add the rest counters

    // Create the custom counter category
    PerformanceCounterCategory.Create("MyCounters", "Custom Performance Counters", PerformanceCounterCategoryType.MultiInstance, counters);
}

И вот код вашего теста:

[TestClass]
public class UnitTest1
{
    PerformanceCounter OverallDelay;
    PerformanceCounter PerDelay;

    [ClassInitialize]
    public static void ClassInitialize(TestContext TestContext)
    {
        // Create the instances of the counters for the current test
        // Initialize it here so it will created only once for this test class
        OverallDelay= new PerformanceCounter("MyCounters", "Delay", "Overall", false));
        PerDelay= new PerformanceCounter("MyCounters", "Delay", "Per", false));
        // .... Add the rest counters instances
    }

    [ClassCleanup]
    public void CleanUp()
    {
        // Reset the counters and remove the counter instances
        OverallDelay.RawValue = 0;
        OverallDelay.EndInit();
        OverallDelay.RemoveInstance();
        OverallDelay.Dispose();
        PerDelay.RawValue = 0;
        PerDelay.EndInit();
        PerDelay.RemoveInstance();
        PerDelay.Dispose();
    }

    [TestMethod]
    public void TestMethod1()
    {
         // Use stopwatch to keep track of the the delay
         Stopwatch overall = new Stopwatch();
         Stopwatch per = new Stopwatch();

         overall.Start();

         for (int i = 0; i < 5; i++)
         {
             per.Start();
             doAction();
             per.Stop();

             // Update the "Per" instance of the "Delay" counter for each doAction on every test
             PerDelay.Incerement(per.ElapsedMilliseconds);
             Sleep(1000);

             per.Reset();
         }

         overall.Stop();

         // Update the "Overall" instance of the "Delay" counter on every test
         OverallDelay.Incerement(overall.ElapsedMilliseconds);
     }
}

Теперь, когда ваши тесты будут выполнены, они сообщают счетчику свои данные. В конце теста нагрузки вы сможете увидеть счетчик на каждом агенте и добавить его к графикам. Он будет сообщаться с значениями MIN, MAX и AVG.

Заключение

  • Я думаю (после нескольких месяцев исследований), что это единственный способ добавить пользовательские данные из ваших тестов в отчет об окончательной нагрузке.
  • Это может показаться слишком трудным. Ну, если вы понимаете, что это не сложно оптимизировать. Я обернул эту функциональность в классе, чтобы легче было инициализировать, обновлять и в конце концов управлять счетчиками.
  • Это очень полезно. Теперь я могу видеть статистические данные из моих тестов, что это будет невозможно с помощью счетчиков по умолчанию. Такие, когда веб-запрос к веб-службе терпит неудачу, я могу поймать ошибку и обновить соответствующий счетчик (например, Timeout, ServiceUnavailable, RequestRejected...).

Надеюсь, я помог.:)

Ответ 2

Я не знаю, как бы вы добавили значение в TestContext и, следовательно, сохранили его с помощью этого механизма. Альтернативой может быть просто написать результаты синхронизации в виде текста для трассировки, отладки или консольных потоков вывода, чтобы они сохранялись в журнале тестового прогона. Чтобы увидеть эти выходы, необходимо рассмотреть три свойства Ведение журнала активных Запуск настроек. Их по умолчанию сохраняет журналы только для первых 200 неудачных тестов. Установка Сохранение частоты регистрации для завершенных тестов до 1 должна сохранять журналы всех тестов до тех пор, пока не будет достигнут Максимальный тестовый журнал. Этапы показаны более подробно в: http://blogs.msdn.com/b/billbar/archive/2009/06/09/vsts-2010-load-test-feature-saving-test-logs.aspx

Одной из недостатков этого подхода является то, что файлы журнала можно видеть только по одному в Visual Studio, щелкнув по ссылкам Test log в одном из окон результатов. Я пытался найти способ извлечения журналов веб-тестов из базы данных SQL результатов теста, вместо того, чтобы нажимать ссылки для каждого журнала в Visual Studio. Я считаю, что журналы unit test хранятся таким же образом. Я описал эту проблему и то, что до сих пор у меня было в https://stackoverflow.com/questions/16914487/how-do-i-extract-test-logs-from-visual-studios-load-test-results

Update. Я считаю, что вопросы, заданные в этом вопросе, не могут быть предоставлены API-интерфейсами, доступными непосредственно в среде тестирования загрузки Visual Studio. Данные и диагностика Адаптеры могут быть написаны для тестов производительности Web и, возможно, также для модульных тестов. Используя такой код адаптера, можно записывать данные из приложения или набора тестов и записывать его в результатах теста. Существует несколько блогов Microsoft и страниц MSDN о написании адаптеров данных и диагностики.

Ответ 3

Самый простой способ - исходный подход OP, есть, по-видимому, некоторые ошибки, с которыми я столкнулся, и другие, похоже, тоже. Один из них заключается в том, что по какой-то причине TestContext.BeginTimer(строка); не всегда существует, см. этот для доказательства, но, похоже, никакого решения. Но есть еще одна проблема неправильного создания и использования свойства.

  • Если у вас нет свойства для хранения TestContext и попробуйте использовать TestContext.BeginTimer();, вы получите сообщение "Cannot Access Non-Static Method 'BeginTimer' in a static context". Причина, по которой некоторые люди делают это, состоит в том, что большинство примеров имеют свойство TestContext как TestContext TestContext; См. 3 по той причине, что примеры используют это.
  • Если вы назначаете свое свойство TestContext в say ClassInitialize или AssemblyInitialize, вы, кажется, получаете что-то, что не совсем правильно, вы получаете один экземпляр тестового контекста, который в прошлом у меня был не проблема для модульных тестов и Coded UI Tests, но тесты нагрузки не справляются с этим. Что вы увидите, если вы это сделаете, это ошибка "There is already an active timer with the name 'TimerName' passed to BeginTimer".

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

Итак, вам нужно что-то вроде следующего:

 private TestContext m_testContext;

    public TestContext TestContext
    {
         get { return m_testContext; }
         set { m_testContext = value; }
    }

Если вы поместите точку прерывания на сеттер, вы увидите, что после Class Initialize, но до TestInitialize вызывается "TestContext setter", а значение присваивается из UnitTestExecuter.SetTestContext(). Теперь тест остается таким же, как вы пытались это сделать.

public void TestMethod1()
{
    TestContext.BeginTimer("Overall");
    for (int i = 0; i < 5; i++)
    {
        TestContext.BeginTimer("Per");
        doAction();
        TestContext.EndTimer("Per");
        Sleep(1000);
    }
    TestContext.EndTimer("Overall");
}

Теперь, когда вы посмотрите на результаты теста нагрузки, вы увидите, что таймеры выходят в сценарии > TestCaseName > Transactions > TimerName

Вот как выглядит мой вывод с моими таймерами Cache, Create-, Login

введите описание изображения здесь

Что содержит

  • Avg. Время отклика
  • Avg. Время транзакции
  • Всего транзакций
  • Операции /Sec

Все они могут быть просмотрены на графике.

В примере OP, если вы провели тест нагрузки с 10 пользователями, каждый из которых запускал тест 1 раз, а DoWork занял 0 секунд, вы увидите:

  • Всего 10 тестов
  • 10 значений для "Общий" по 5 секунд каждый,
  • 50 значений для "Пер" по 0 секунд каждый.

Я думаю, что это предполагаемый результат.

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

С другой стороны, это правильная реализация TestContext для правильной работы Data Driven Testing, поскольку каждый тест может получить свои правильные данные из контекста.