Customizace buildu ve VS — automatické spuštění unit testů

Kompilátor nám u staticky typovaných jazyků pomůže odhalit spoustu chyb už při tzv. compile time, nebo-li když ve Visual Studiu stiskneme známou klávesovou zkratku CTRL+SHIFT+B. Kdo se nespokojí pouze s kontrolou typů a chce psát spolehlivější software, kromě kompilátoru užívá automatizovaných testů, především unit-testů. V tomto smyslu by se unit-testy daly chápat jako nadstavba kontroly kompilátorem, tak proč je také nespouštět hned po každé kompilaci? (Mimochodem uvědomme si, že unit-testy by měly být z definice rychlé, takže by jejich běh kompilaci neměl tolik zdržet.)

Jistě znáte soubory s příponou vcproj, případně vbproj pro Visual Basic. Jsou to projekty pro Visual Studio, takže je, přirozeně, otevíráte ve Visual Studiu, ale zkusili jste je někdy otevřít v obyčejném textovém editoru? Pokud byste tak učinili, uviděli byste něco podobného jako v následující ukázce:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://...">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <!-- a tak dále ... -->

už podle první řádky je jasné, že se jedná o xml. Konkrétně je to xml dialect určený pro MSBuild. Tento program, který naleznete na cestě „C:\Windows\Microsoft.NET\Framework\{verzeNET}\MSBuild.exe“, je interpretem speciálního deklarativního na xml postaveného jazyka. Podrobnou syntaxi naleznete v jiných článcích na internetu, pro účely spuštění unit testů po buildu si vysvětlíme nutné základy.

Formát MSBuild umožňuje definovat tzv. cíle (target). Atribut DefaultTargets kořenového uzlu Project určuje cíl, který bude defaultně spuštěn. Cíl může spouštět další cíle a úkoly (task). Význam úkolů si objasníme později. Kromě úkolů a cílů můžeme pro zpřehlednění kódu definovat něco jako proměnné. V souboru csproj, který pro nás vygenerovalo Visual Studio, můžeme nalézt zakomentované následující cíle:

  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. -->
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>

Tyto cíle, jak podle jejich jména jistě tušíte, budou spuštěny před a po buildu. Takže pro spuštění unit testů není potřeba nic většího než je odkomentovat a přidat nějaký obsah. Tím obsahem bude zavolání nunit.exe (nahraďte si svým oblíbeným unit-test frameworkem) a k tomu slouží předdefinovaný úkol (task) Exec, který je standardní součástí instalace MSBuildu.

<Target Name="AfterBuild">
  <Exec
    Condition="$(Configuration) == Debug"
    Command="
       &quot;C:\Program Files\NUnit\bin\net-2.0\nunit-console.exe&quot;
       &quot;$(OutputPath)Tests.dll&quot;" />
</Target>

Atribut Condition určuje podmínku, za které bude úkol spuštěn. Atribut Command určuje příkaz — sem napíšeme to samé, jako kdybychom chtěli unit testy spustit z příkazové řádky. Uzávorkované a znakem dolaru uvozené části (např. $(OutputPath)), jsou zmíněné „proměnné“. Využili jsme dvou proměnných, které pro nás zadefinovalo už Visual Studio. OutputPath určuje cestu, kde budou umístěny sestavy (assembly), které jsou výsledkem buildu. Význam Configuration byl měl být jasný.

Nyní je vše nastaveno. Po každém buildu se spustí unit-testy a pokud některý z nich selže, nunit.exe vrátí kód chyby a celý build selže. Obecně lze následující metodu použít pro spuštění jakékoliv akce ihned po buildu nebo i před buildem.

Update: objevil jsem zajímavý nástroj AutoTest.Net, který umí zařídit automatické spouštění testů hned po uložení souboru.

Comments 2

  1. Aleš Roubíček wrote:

    Natvrdo zadaná cesta k nunit konzoli nemusí být úplně šťastné řešení. Né každý, kdy stebou bude na projektu dělat, ji bude mít ve stejném umístění. Lepší je do solution přidat složku tool, kam dáš NUnit a castu zadáš relativně.

    Spouštět testy při každém buildu může být časově dost náročné, v tomhle případě je lepší udělat buďto další build konfiguraci a testy spouštět přes ní.

    Efektivnější je stejně použít nějaký integrovaý test runner do IDE, testy si pustíš kdy potřebuješ a zbytečně to neprodlužuje samotný build. zkus http://www.gallio.org/ nebo http://testdriven.net/

    Posted 11 Říj 2010 at 7.43
  2. Steves wrote:

    Cesta k NUnit: ano souhlasím, cestu je dobré mít v samostatném souboru v podobě těch „proměnných“ (alias properties pro znalce MSBuildu). Navíc se hodí použít nějaké specializované tasky, jako například NUnit z MSBuild Community Tasks. V „článku“ jsem ale nechtěl zacházet do takových podrobností.

    Na myšlenku spouštění testů při každém buildu, resp. dokonce spuštění buildu a testů při každém ctrl+s, mě přivedl Miško Hevery (http://misko.hevery.com/). Na jeho přednášce TDD by Google tohle praktikoval v Ecplise (VS se mi nepodařilo v tomto směru jednoduše nastavit. Dokážu si představit, že to půjde přes makro, vlastní plugin nebo tak nějak, ale to už je zbytečně moc práce…). V NUnit si mohu „pomalé unit-testy“ označit atributem a nespouštět je pokaždé. Když už člověk dělá testy, je dobré je pouštět často a ctrl+shift+b už jsem si tak nějak zvykl používat dost často.

    Na většině test runnerů ve VS mi trochu vadí, že do toho procesu nezapadají tak nějak plynule, představoval bych si to tak, že runner se spustí v malém okénku, ukáže mi, že všechny testy jsou ok a zavře se a pokud je něco v nepořádku, tak kliknu na nějakou malinkou ikonu a objeví se podrobnější informace. (Intergovaný runner pro MSTesty tohle sice částečně splňuje, ale kdo používá MSTesty, že? :-) ) Ale znám pouze integrovaný runner a ten distribuovaný s R#, takže mi možná poradíš nějaký jiný, podle mých představ.

    Posted 11 Říj 2010 at 15.13

Post a Comment

Your email is never published nor shared. Required fields are marked *