Преобразование XML отчетов Nunit в HTML

Есть желание !! И есть нужда :)

вот очень простая трансформация, улучшать можно сколько хочешь 

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
  <html>
   <body>
    <h1>total is <xsl:value-of select="test-results/@total"/></h1>
    <h1>errors: <xsl:value-of select="test-results/@errors"/></h1>
    <h1>failures <xsl:value-of select="test-results/@failures"/></h1>
    <h1>time taken <xsl:value-of select="test-results/test-suite/@time"/></h1>
    <table>
     <tr>
      <td>
       <b>name</b>
      </td>
      <td>
       <b>status</b>
      </td>
      <td>
       <b>time</b>
      </td>
     </tr>
     <xsl:for-each select="//test-case">
      <tr>
       <td>
        <xsl:value-of select="@name"/>
       </td>
       <td>
        <xsl:value-of select="@result"/>
       </td>
       <td>
        <xsl:value-of select="@time"/>
       </td>
      </tr>
     </xsl:for-each>
    </table>
   </body>
  </html>
</xsl:template>
</xsl:stylesheet>

 

и результат получился вот таким вот

 

total is 10

errors: 1 vs failures 2

time taken 211.862

name status time
SeleniumTests.Prometeus_main.A1_First_Authorization Error 32.063
SeleniumTests.Prometeus_main.A2_First_News Success 14.638
SeleniumTests.Prometeus_main.A3_Valid_Login Success 13.630
SeleniumTests.Prometeus_main.A4_Change_Password Success 20.311
SeleniumTests.Prometeus_main.A5_Change_Password_back Success 16.904
SeleniumTests.Prometeus_main.A6_Password_Requirments Success 16.177
SeleniumTests.Prometeus_main.B1_Search_test_by_full_name Success 26.632
SeleniumTests.Prometeus_main.B2_Search_test_by_part_of_name Success 23.744
SeleniumTests.Prometeus_main.B3_Search_test_by_mask Failure 21.292
SeleniumTests.Prometeus_main.C3_Check_button_zakaz_back_confirm Failure 26.183

 

хорошо, тогда можно смотреть на трансформацию :)

http://automated-testing.info/forum/preobrazovanie-xml-otchetov-nunit-v-html#comment-3709

Большое вам спасибо!!! Получилось, уже немного понял принцип, как появится время постараюсь сделать разные украшательства, но главное что есть каркас. Осталось только украсить его и добавить рюшечек, плюшек и прочих приятностей :)

 

ну вот и чудно, пользуйтесь на здоровье

только потом покажите окончательный вид, который вы разработаете для себя

чтобы мы увидили, что означает простой репорт для ваших нужд в конечном виде :)

Сделал себе отчетик :) Может быть кому понадобится:

 

 

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<head>
    <title>Отчет об автоматизированном тестировании</title>
</head>
<body>
    <h1 align="center">Отчет об автоматизированном тестировании</h1>
        
  <table border="0" cellpadding="2" cellspacing="0" width="95%" style="border: #dcdcdc 1px solid;">
      <xsl:variable name="percent" select="format-number((test-results/@total - test-results/@errors) div test-results/@total * 100,'#.00')"/>
      <xsl:if test="$percent > 95">
        <tr>
          <td bgcolor='#33CC00' align="left">
              Generated by: <xsl:value-of select="test-results/@date"/> - <xsl:value-of select="test-results/@time"/>            
          </td>
        </tr>
        <tr>
          <td bgcolor='#33CC00'>
           $Name_of_Project
          </td>
        </tr>
      </xsl:if>

      <xsl:if test='$percent &lt;=  95'>
        <tr>
          <td bgcolor='#FF3300'>
              $Name_of_Project
          </td>          
        </tr>
        <tr>
          <td bgcolor='#FF3300' align="left">
              Generated: <xsl:value-of select="test-results/@date"/> - <xsl:value-of select="test-results/@time"/>           
          </td>
        </tr>
      </xsl:if>
  </table>
            
<h2 align="center">Результаты</h2>
    <table border="0" cellpadding="2" cellspacing="0" width="95%" style="border: #dcdcdc 1px solid;">
        <tr valign="top">
            <td>
                <b>Тестовых сценариев</b>
            </td>
            <td>
                <b>Успешных</b>
            </td>
           
            <td>
                <b>С ошибками</b>
            </td>
            <td>
                <b>Успешных</b>
            </td>           
            <td>
                <b>Общее время выполнения</b>
            </td>
        </tr>
        <tr>
            <td>
               <xsl:value-of select="test-results/@total"/>
            </td>
            <td>
              <xsl:value-of select="test-results/@total - test-results/@errors"/>
            </td>
            <td>
                <xsl:value-of select="test-results/@errors"/>
            </td>
            <td>              
              <xsl:value-of select="format-number((test-results/@total - test-results/@errors) div test-results/@total * 100,'#.00')"/> %              
            </td>             
            <td>
               <xsl:value-of select="test-results/test-suite/@time"/>
            </td>
        </tr>
    </table>

  <h2 align="center">Техническая информация</h2>
  <table border="0" cellpadding="2" cellspacing="0" width="95%" style="border: #dcdcdc 1px solid;">
    <tr>
      <td>
        <b>Программное обеспечение</b>
      </td>
      <td>
        <b>Версия</b>
      </td>
    </tr>
    <tr>
      <td>
        Browser
      </td>
      <td>
        $Browser
      </td>
      </tr>
    <tr>
      <td>
        Nunit version
      </td>
      <td>
        <xsl:value-of select="test-results/environment/@nunit-version"/>
      </td>
    </tr>
    <tr>
      <td>
        Clr-version
      </td>
      <td>
        <xsl:value-of select="test-results/environment/@clr-version"/>
      </td>
    </tr>
    <tr>
      <td>
        Platform
      </td>
      <td>
        <xsl:value-of select="test-results/environment/@platform"/>
      </td>
    </tr>
    <tr>
      <td>
        Operating System
      </td>
      <td>
        <xsl:value-of select="test-results/environment/@os-version"/>
      </td>
    </tr>
 </table>
         
<h2 align="center" >Результаты в разрезе сценариев</h2>
      <table border="0" cellpadding="2" cellspacing="0" width="95%" style="border: #dcdcdc 1px solid;">
        <tr>
            <td width="25%">
              <center><b>Тестовый сценарий</b></center>
            </td>            
            
          <td width="15%" align="left">
            <center><b>Результат</b></center>
          </td>
          <td width="25%" align="left">
            <center><b>Детали ошибки</b></center>
          </td>  

          <td width="15%" align="left">
            <center><b>Время выполнения</b></center>
          </td>
          
        </tr>

      <xsl:for-each select="//test-case">
        <tr>
          <td>
            <xsl:value-of select="@name"/>
          </td>
                   
            <xsl:if test="@result!='Error'">
             <td bgcolor='#33CC00'>
               <center><xsl:value-of select="@result"/></center>
             </td>
              <td>
                <center> — </center>
              </td>
            </xsl:if>

          <xsl:if test="@result!='Success'">
            <td bgcolor='#FF3300'>
              <center><xsl:value-of select="@result"/></center>
            </td>
            <td>
              <center><xsl:value-of select="//message"/></center>
            </td>
          </xsl:if>

          <td>
            <center><xsl:value-of select="@time"/></center>
          </td>
         
        </tr>
      </xsl:for-each>      
   </table>
        
 <!-- <hr size="1" width="95%" align="left"></hr> -->
 
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Круто. Я впечатлен :)  

Вот если бы я был бы дизайнером :) Возможно кто-то будет себе делать красивую страничку и захочет поделиться... А по поводу информативности отчетов, тут я бы с вами поспорил, информации тут достаточно, сам текст ошибки (суть) + при желани можно вывести stack-trace, но как по-мне это уже перебор. Хотя конечно при отладке мы получим куда более полную информацию.

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

 

Э..ээ ээ а по поводу информативности отчетов, я с вами спор и не начинал даже :)
А вот почему разработчики не предусмотрели нормальных отчетов, то тут у меня ответ есть. 
NUnit в первую очередь – инструмент для модульных тестов, и интеграционных тестов, когда код приложения непосредственно используется из тестов (Белый Ящик). Если тест завалился – то это не из за того, что у дива в спане айдишник поменялся, а из-за того, что что-то серьезное поменялось в коде приложения. 
Для юнит-тестов отчеты в HTML не особо нужны. И если  в тестах произойдет исключение, например NullReferenceException  – то колл-стек приведет программиста из теста прямо в том место, где оно было выброшено в приложении. 
Для программистов вполне хватает той функциональности, что уже есть в NUnit и не нужны красивые отчеты. 
Мы юзаем NUnit для авто тестов. Просто чтобы не изобретать свой велосипед, ведь инструмент по большей частью нам подходит.
Красивые отчеты больше нужны тестировщикам-автоматизаторам, а не программистам. 
А с новым опытом работы с XSLT – я вас поздравляю. 
 

да на картинке смотриться отлично

поздравляю с хорошим отчетом :) 

да по поводу спора интересно почитать :) что же Дима такое спорное написал :)

Прошу прощения, это писал "apetrovskiy" - "NUit'овский XML всё равно не содержит подробностей"

А в чём вопрос? Наш репортер (TMX) помимо названия, статуса, времени выполнения содержит имя файла и строку кода, коллекцию ошибок (ErrorRecord), скриншоты контрола или десктопа (как минимум, по ошибке). Подумываю добавить (сделано в другом нашем фреймворке) выдёргивание данных с операционки ("другой" фреймворк копирует кусок лога нашего агента от начала работы тест-кейса до конца. Если агент падает или спотыкается, инфа подшивается автоматически).

К примеру, без скриншотов (которые или кидаются в SQLite базку, или вставляются в HTML репорт как ссылки на картинки с открытием в новом табе) разбираться с гуёвыми тестами не так удобно. Хотя, если ваши тесты атомарны, вы можете запустить красные результаты заново. Но всё равно, без актуальной инфы (логи, скриншоты) отчёты в NUnit'е пресноваты.

Я ещё вывожу на экран NUnit'а исходный код всех тестов. В отчёт это попасть не может (с экрана в отчёт никак не положить), но скопипэйстить в файл сэмплового кода можно.

 

Кстати, это вам ещё везет, что вы можете использовать NUnit: летом я приводил пример (не знаю, может сейчас они починили баги): omenahotels.com (довольно удобно, если надо провести праздники в Финляндии,и а коттедж на три дня брать или невыгодно, или все уже расхватаны).

Можете сами оформить заказ чисто из профессионального интереса - такое впечатление, что странички сами по себе потестили (чем-нибудь юниттестовым, к примеру), а вот когда надо взять три периода в разных гостиницах, да на трёх человек, вот тут начинается жесть. Я не  только про то, что каждому постояльцу в комнате надо иметь уникальный номер тлф и имейл (пятилетний сын только-только начал что-то набирать на клавиатуре :) и мобильника пока не имеет, чтобы не заиграл его), в этих ходах можно лишиться всех набранных паспортных данных и адресов, и набирать заново.

Это я заглубился уже в сторону "юнит-тесты против сценарийных тестов". :)

Подумываю перейти на Gallio: из минусов пока заметил 1) какая-то ругань при фильтрации по категориям (наверное, у меня не везде категории были) 2) довольно неудобно выбрать две категории в дереве из всех - это ж надо схлопнуть почти все категории, чтобы увидеть нужные. Он ведь показывает категории из всех файлов проекта. 3) при добавлении файла к проекту, раскрывает всё дерево проекта (но это делается очень редко).

командлайн в нъюните не пользовал, поэтому сравнить не могу.

А, ещё такой полу-минус: у меня есть заготовки тестов на не первой актуальности фичи (файл с атрибутами/SetUP-TearDown, но без самих тестов). NUnit их не замечал (только цвет менял, а не счётчики), а Gallio считает их проблемными.

Ещё нашёл минорный недостаток: шесть ошибок в тесте, пошёл в Execution Log, справа только четыре красные зарубки (как будто только по одной категории показало, а не по двум). На самом деле, мелочь - оставшиесе две тоже в этих же местах лога расположены.

Перехвалил: в некоторых случаях (например, переход от категорий к неймспейсам, и не только), может потерять часть или все ветки проекта - приходится загружать проект заново.

Сама по себе загрузка проекта и тестов не быстрая, но это ещё может быть связано и с тем, что в нъюнит я грузил по одному проекту (но там это заметно быстрее).

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

Пока резюме такое: можно работать с Moles (мне их не удалось завести даже на специально выкачанном нъюнит 2.5.2), но надо опасаться багов гуя ( и привыкать к ним).

а где же Дима Жарий, который хвалил Gallio? :)

К примеру, юзкейс: я архивирую сьюту моих проектов целиком (обычным севензипом ультра), включая нъюнитовские (теперь - галлиевские) проекты, стайлкоповские рулы, и т.д., и т.п. Юзкейс весьма неплох (я не юзаю сорс контрол, поскольку инет не всегда доступен, да и через корпоративный прокси не хочу долбиться), всё своё носится с собой на флешках и копируется в облако для пущей сохранности.

По известной теореме, на каждый юзкейс есть ненулевое число багов в произвольно взятом софте. Баг на мой юзкейс: при создании проекта, галлио прописывает путь вида c:\users\%username%\blah-blah-blah. В чём баг, спросите вы? Да вот, поделка-то как раз и не использует %username%. На машинках w8 у меня юзернейм генерится из хотмэйловского аккаунта, на других - сам задаю. Да и вообще я могу быть не один, но команда людей со строго разными именами. :) Итак, на всех машинках, кроме хостов w8 я имею эксепшн на ровном месте, после выполнения тестов, выдающий текст, что по пути c:\users\alexander\...  gallio поджидал большой облом ввиду отсутствия пути. Кстати, создать путь галлио даже не собирается. Просто эксепшн, ниачём.

Опенсорс - это всегда весело!

Другой незначительный, но достающий баг - это NUnit add-in. NUnit поддерживает дескришены вида [TestFixture(Description="blah")], [Test(Description="description")]. Я их усердно заполнял в течение многих месяцев существования данной сьюты (ради хорошего тона, они в нъюните, на самом деле, не особо нужны). Кое-где у меня не было дескрипшена. Это стабильно вызывало серьёзнейшее недовольство галлио (хотя дескрипшен необязателен).

Сегодня я перешёл на MBUnit (тут надо похвалить - всго две автозамены в атрибутах, автозамена юзинга, автоазмена NotNull -> IsNotNull и немного замен в местах, где был полный путь вида NUnit.Framework.Assert...) и баг перестал меня беспокоить (нет-нет, он не пролечился, просто аддын нъюнит более не используется).

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

 

А вообще продукт мощный, и параллельный запуск, и более-менее удобный доступ через рефлексию (всё же, в Moles и Moq мне больше нравится оттопыривание свойств/методов объекта). Жаль, что книжка продукта состоит, главным образом, из пустых страниц.

 

На счет путей, можно отредактировать файл проекта Gallio и указать относительные пути. 
По типу       
  <testPackage shadowCopy="true" debug="false" applicationBaseDirectory="..\..\Bin\Wmm\" workingDirectory="..\..\Bin\Wmm">
    <files>
      <file>..\..\Bin\Wmm\Wmm.Tests.dll</file>
    </files>
    <hintDirectories />
    <excludedFrameworkIds />
    <properties />
  </testPackage>
 
К сожалению, с отчетами относительные пути не очень хорошо работают:
  <reportDirectory>..\Reports</reportDirectory>
После прогона в Icarus, отчеты будут складываться в указанную папку,  но не будут отображается в списке прогнанных отчетов на UI. Потому что Icarus читает файлы отчетов только из дефолтной папки на пользователя. 
В случае нестандартной папки, придется конверитить XML самостоятельно, при помощи команды:
 
"C:\Program Files\Gallio\bin\Gallio.Utility.exe" FormatReport D:\TestReports\test-report-20121013-132029.xml /ReportType:Html
 

Вот ещё баг - опишу "от аналога". Писали мы некогда один популярный редактор, с автосохранением конечно, и прочими финтифлюшками. И вот был такой баг (и, в какой-то степени остался): открыть файл в чём-то и в редакторе, или просто два раза в редакторе. Затем несколько раз файл поменять и сохранить. Чё делает умный редактор (даже если отредактировали в том же инстансе редактора в другом табе)? Спрашивает, загрузить ли обновлённую версию файла. 

Редактор просто ловит событие в файловой системе и выкидывает этот вопрос. Не важно, что из n сохранений можно спокойно не загружать первые (n - 1).

 

Точно также поступает и галлио. Я компилю быстро и охотно (Core i7, 8GB RAM, Vertex 4 - контора наконец-то шевельнула задом и проапгрейдила компы не только документаторам, и тем, кто пишет и компилит :)). И вот эта св загружает мои тесты (эксплорит, как это там называется) много раз. А загрузка - процесс не быстрый. Мои менее чем тысяча тестов из пяти пока проектов загружаются не меньше времени, чем компилятся. И так n раз.

 

Процесс прервать можно, но сложно и стрёмно: тут на сцену выходит другой баг: из таба Test Explorer легко теряются несколько или все проекты. Т.е., в Project Explorer они есть. Иногда помогает сделать Recent project этого самого проекта. Иногда помогает перейти из показа Namespace в Category, и обратно. Иногда - только перезапуском галлио.

 

Как альтернативный вариант, если ведется работа над одним тестом, предлагаю запускать его из Visual Studio, без Gallio Icarus.
Для этого, если у вас проект – типа ClassLibrary, его нужно сделать Test Project ом. 
Для этого , откройте ClassLibrary1.csproj
И в PropertyGroup добавьте строку:
 
    <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
 
Выглядеть должно все примерно так:
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>8.0.30703</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{571A8714-C36F-497D-A076-E6C1DEDFFC94}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>ClassLibrary1</RootNamespace>
    <AssemblyName>ClassLibrary1</AssemblyName>
    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
  </PropertyGroup>