Профайлинг NUnit-тестов .NET Framework 4


    С профайлингом приложений наверняка сталкивался каждый, но как часто вам приходилось профайлить тесты?

    Как показал мой личный опыт, чтобы успешно выполнить эту задачу для сборки, собранной под .NET Framework 4, требуется выполнить ряд действий, на поиск которых мне пришлось потратить определенное время. Поэтому я решил обобщить полученный опыт в единую компиляцию и сделать ее доступной для того, чтобы другие смогли избежать тех граблей, на которые пришлось наступить мне.

    Задача стояла вполне реальная — необходимо было замерить расход памяти при подъеме большого количества тестов — для того, чтобы убрать утечки памяти.

    В рабочем проекте в качестве тестовой среды использовался NUnit 2.4, а в качестве профайлера был выбран .NET Memory Profiler 4.0.

    Тестовые данные


    Сделаем сборку, содержащую пример теста.

    Для этого в Visual Studio 2010 создадим новый проект WPF User Control Library, добавим в референсы nunit.framework.dll и создадим файл TestClasses для тестов.


    Рис.1 Референсы и состав проекта


    Рис.2 Окно опций проекта

    Напишем один единственный тест, код которого приведен ниже:

    using System;
    using NUnit.Framework;
    using System.Windows.Controls;
    using System.Windows;
     
    namespace WpfClassLibrary {
        [TestFixture]
        public class MyTests {
            [Test]
            public void TestForProfiling() {
                TextBox textBlock = new TextBox();
                textBlock.Text = "UIElement Test";
                Assert.IsTrue(textBlock.Text.Length > 0);
                MessageBox.Show("Ready for Collect Snapshot...");
            }
        }
    }
    


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

    Настройка профайлера


    C чего начать профайлинг теста?

    Запустим профайлер и укажем в качестве запускаемого приложения консольную версию NUnit. Для этого указываем файл nunut-console.exe (в моем случае он был расположен по следующему пути: C:\Program Files (x86)\TestDriven.NET 3\NUnit\2.4\ ).

    Теперь останется задать аргументы. Достаточно запустить nunut-console.exe с ключом "/?" чтобы получить интересующий нас список. Формат запуска файла выглядит так:

    NUNIT-CONSOLE [inputfiles] [options]

    Т.е необходимо указать путь до сборки с тестами, указав опции. Нас будет интересовать только одна:

    /run=STR Name of the test to run

    Хочу заметить, что в качестве имени теста необходимо указывать полный! путь до тестового метода, включая и пространство имен.

    Приходим к тому, что пропишем следующую строку в качестве аргументов для профайлера:

    C:\Sandbox\WpfClassLibrary\WpfClassLibrary\bin\Debug\WpfClassLibrary.dll /run=WpfClassLibrary.MyTests.TestForProfiling



    Рис.3 Окно настроек профайлера для запуска теста

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


    Как позволить NUnit запусть сборки собранные в VS2010 ?


    Попробуем запустить наш тест, используя nunit-console.exe, а не профайлер.

    В результате получаем ошибку System.BadImageFormatException:
    Unhandled Exception:
    System.BadImageFormatException: Could not load file or assembly 'C:\Sandbox\WpfClassLibrary\WpfClassLibrary\bin\Debug\WpfClassLibrary.dll' or one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded.
    File name: 'C:\Sandbox\WpfClassLibrary\WpfClassLibrary\bin\Debug\WpfClassLibrary.dll'


    Т.к. что наша сборка собрана под Framework 4, она не может быть загружена для запуска теста.

    Находим файл nunit-console.exe.config, расположенный рядом с консольной версией NUnit. На моей машине он расположен тут:
    «C:\Program Files (x86)\TestDriven.NET 3\NUnit\2.4\nunit-console.exe.config»

    Ищем секцию startup, в которой описание нам говорит:
    <!--
       The startup section may be used to specify the runtime versions
       supported in the order that they will be used if more than one
       is present.
      -->

    * This source code was highlighted with Source Code Highlighter.

    Эта секция содержит информацию о поддержку .NET Framework версий 1 и 2, но никак не 4.
    Создаем свою startup-секцию (или модифицируем существующую) и добавляем туда строку для поддержки запуска сборки 4-го фреймворка. Строка для добавления выглядит так:

    <startup>
       <supportedRuntime version="v4.0.30319" />
    </startup>

    * This source code was highlighted with Source Code Highlighter.


    Первая проблема решена — NUnit теперь умеет запускать тесты, собранные под .NET Framework 4.


    Конфигурируем запуск сборки с тестами в STA


    Вернемся к нашему тесту. Он прекрасно запускается и проходит, если запускать его непосредственно из Visual Studio 2010 с помощью интегрированных в нее средств TDD. Если же попытаться выполнить тест из nunit.exe или ее консольной версии (для консольной не забудем указать параметром тестовую сборку), то произойдет следующая ошибка:
    MyTestLib.MyTests.TestForProfiling:
    System.InvalidOperationException: The calling thread must be STA, because many UI components require this.



    Рис.4 Ошибка при запуске теста в NUnit

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

    Запуск тестов для WinForms или ASP.NET может отличаться от рассматриваемого WPF примера, но, тем не менее, существует ряд ограничений (таких как OLE drag-n-drop, работа с клипбордом и т.д), которые будут требовать запускать тесты в STA.

    В более новой версии NUnit 2.5 даже существует специальный RequiresSTAAttribute атрибут для этих целей.

    Для тех, кто ограничен использованием NUnit версии 2.4 можно воспользоваться конфигурационными файлами.

    Для тестируемой сборки создадим такой файл с указанием необходимости запуска тестов в STA. Хочу заметить, что он должен иметь имя, совпадающее с именем сборки и постфиксом config, и должен быть расположен рядом сней.

    Создадим файл с именем WpfClassLibrary.dll.config следующего содержания:
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <configSections>
        <sectionGroup name="NUnit">
          <section name="TestRunner" type="System.Configuration.NameValueSectionHandler" />
        </sectionGroup>
      </configSections>
      <NUnit>
        <TestRunner>
          <add key="ApartmentState" value="STA" />
        </TestRunner>
      </NUnit>
    </configuration>


    * This source code was highlighted with Source Code Highlighter.



    Вторая проблема решена — файл тестовой сборки теперь имеет конфигурационный файл, определяющий правильный способ запуска из NUnit.



    Профайлим тест


    Пришло время проверить результат. Запустим профайлер и убедимся, что тест был вызван.


    Рис.5 Профайлинг теста

    Задача решена — сборка с тестом корректно подгружена и тест запущен.

    В дополнение хочу сказать, что существует другой вариант запуска профайлера. Суть метода в том, что можно указать системе запускать определенный процесс (для TDD это ProcessInvocation86.exe) под отладчиком (профайлером). Это делается прописыванием в реестре ключа Image File Execution Options.
    Но это уже тема для отдельной статьи…

    На этом всё! Надеюсь, что эта информация окажется полезной и сэкономит время на выполнение подобной задачи.

    Удачного тестирования и профайлинга!
    Developer Soft
    Компания
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 12

    • НЛО прилетело и опубликовало эту надпись здесь
        +1
        И использовать картинку в начале поста для привлечения внимания — это тоже читерство. И вообще, пользоваться форматированием текста и вставлять скриншоты — это ведь тоже для того, чтобы люди обратили внимание на твою статью и прочитали её… какой УЖАС! :-)
          +2
          Если честно, я долго и безрезультатно гуглил что-либо на тему «профайлинг тестов» ;) Предлагаю попробывать самому…
          • НЛО прилетело и опубликовало эту надпись здесь
              +1
              Следующий раз слона нарисую ;)
                +3
                Здесь — не люди, а конечные автоматы. Нет тега — нет эмоций!
            0
            R# + dotTrace?
              0
              Не совсем понимаю, правда, при чем здесь ReSharper ;) А насчет dotTrace вы вправе использовать любой известный вам профайлер. Приведенный выше NET.Memory Profiler был не в целях какой-то рекламы, а как реальная примененная тулза в моем конкретном случае. С другим профайлером у вас могут быть свои ньюансы, но общий смысл статьи (конфиги сборок, nunit), я думаю, особо не изменится. Если у вас есть что рассказать, с удовольствием выслушаю…
                0
                ReSharper здесь как лончер тестов, который умеет запускать их через под dotTrace-ом.
                Когда-то JetBrains выпускали отдельный UnitTestRunner, но… «fu, e non e»©.
                  0
                  Ясно. Это и логично — иметь в рамках одной компании тесно интегрируемые между собой инструменты.
              +1
              Еще можно использовать связку AQTime Profiler + CodeRush Test Runner. Для этого надо в реестре поставить как дебаггер AQTime Profiler для CR_ExtUnitTestRunner.exe (CR_ExtUnitTestRunnerNet4.exe, CR_ExtUnitTestRunnerx64.exe, CR_ExtUnitTestRunnerNet4x64.exe). В msdn написано как это сделать. Не забыть передать параметры командной строки %1 %2 %3 в дебагер, для запуска тест ранера. После этой настройки при запуске тестов из студии поднимется профайлер и запустит тест ранер.
              Удачного профайла :)
                0
                Потихоньку перевожу на русский документацию по NUnit 2.6. Результаты выкладываю здесь. Посильное участие (проверка перевода, перевод ещё не переведённого) приветствуется.

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

                Самое читаемое