Středa, červenec 07, 2004

Základní práce s NUnit 2.1.4

Když jsem psal spot o extrémním programování, tak mne napadlo, že by bylo vhodné čtenáře seznámit s nejrozšířenějším nástrojem pro tvorbu a obsluhu programových testů pro všechny .NET programovací jazyky, frameworkem NUnit. NUnit pochází z testovacího aplikačního rámce JUnit, který zajišťuje práci s testy pro programovací jazyk Java.

Instalace

Z domovského serveru www.nunit.org jsem stáhl soubor Nunit-V2.1.4.msi o velikosti 1.7 MB. Po spuštění proběhla instalace standardním instalátorem MS Windows. Instalace si ukousla 3.4 MB místa na disku. Správnost instalace jsem ověřit podle návodu tak, že jsme spustil grafického klienta NUnit, jehož zástupce instalace umístila na plochu. Všech 478 testů proběhlo téměř ihned a bez jakýchkoliv problémů.

Konfigurace NUnit se provádí v konfiguračním souboru nunitconsole.exe.config pro práci s příkazovým řádkem a nunit-gui.exe.config pro práci s grafickým prostředím. Konfigurační údaje pro vlastní testy a aplikaci by měly být v odděleném konfiguračním souboru nebo souborech. Hlavní účel konfiguračního souboru NUnit je ve výběrů různých verzí .NET frameworku.

Assertion

Asserce (tvrzení něčeho nebo trvání na něčem) je základ všech testů ve všech testovacích knihovnách a NUnit není žádná výjimka. NUnit nabízí velké množství přetížené statické metody Assert a Assert třídu. Jestliže při provádění testu assertion selže,  je oznámena chyba. Jestliže test obsahuje více assertions, ostatní po selhání prvního nejsou testovány. Z tohoto důvodu je nejlepší provádět jedno assertion na jeden test. Zní to triviálně, ale o tom jsou testy. NUnit zavádí tři typy assertions:

Porovnávání

Porovnávání je nejobvyklejším testem, protože při chybě oznámí nejen očekávanou, ale i skutečnou hodnotu. Předpokládaná hodnota je vždy prvním argumentem, druhým je aktuální hodnota a nepovinný, třetí argument označuje případné chybové hlášení.

Assert.AreEqual( int expected, int actual );
Assert.AreEqual( int expected, int actual, string message );

Assert.AreEqual( decimal expected, decimal actual );
Assert.AreEqual( decimal expected, decimal actual, string message );

Assert.AreEqual( float expected, float actual, float tolerance );
Assert.AreEqual( float expected, float actual, float tolerance,
string message );

Assert.AreEqual( double expected, double actual, double tolerance );
Assert.AreEqual( double expected, double actual, double tolerance,
string message );

Assert.AreEqual( object expected, object actual );
Assert.AreEqual( object expected, object actual, string message );

Assert.AreSame( object expected, object actual );
Assert.AreSame( object expected, object actual, string message ); 

Splnění podmínky

Metody testují zadanou podmínku. Podmínka je vždy prvním argumentem, druhý, nepovinný argument obsahuje případné chybové hlášení.

Assert.IsTrue( bool condition );
Assert.IsTrue( bool condition, string message );

Assert.IsFalse( bool condition);
Assert.IsFalse( bool condition, string message );

Assert.IsNull( object anObject );
Assert.IsNull( object anObject, string message );

Assert.IsNotNull( object anObject );
Assert.IsNotNull( object anObject, string message );

Vygenerování selhání

Metoda Fail se používá v případech, kdy potřebujete generovat selhání na testech, které nejsou zapouzdřeny v ostatních assertion's metodách.

Assert.Fail();
Assert.Fail( string message );

Důležité atributy

Zde uvedu pár základních atributů.

[Test]
Označí specifickou metodu uvnitř třídy, která již byla označena atributem [TestFixture], jako testovací metodu. Pro zpětnou kompatibilitu s předchozími verzemi NUnit, bude stejně označena i metoda, která začíná znaky "test", bez ohledu na velikost písmen.
[TestFixture]
Atribut označí třídu, která obsahuje testovací metody. Je obsažen v NUnit.Framework jmenném prostoru. V předchozích verzích NUnit bylo požadováno, aby testovací třída byla potomkem třídy TestCase. Řešení pomocí atributu [TestFixture] je mnohem flexibilnější, protože je možné třídu s testy odvodit od libovolného rodiče.
Je zde ovšem několik omezení, například testovací třída musí mít implicitní konstruktor.
[TestFixtureSetUp/TestFixtureTearDown]
Těmito atributy se označují metody před zahájením testů [TestFixtureSetUp] a po jejich ukončení [TestFixtureTearDown]. Používají se uvnitř atributu [TestFixture]. Metody jsou provedeny vždy jenom jednou. Každým atributem může být označena pouze jedna metoda. Pokud označíte stejným atributem více metod, nebude zahlášena žádná chyba.
[Expected Exception]
Umožňuje předepsat, že provádění testu vyhodí specifikovanou výjimku. Předávaný parametr atributu je typ výjimky. Test projde, pokud je vyhozena specifikovaný výjimka nebo její potomek. Pokud je vyhozena jiná výjimka, test selže.
 
namespace NUnit.Tests
{
using System;
using NUnit.Framework;

[TestFixture]
public class SuccessTests	{
	[Test]
	[ExpectedException(typeof(InvalidOperationException))]
	public void ExpectAnException()	{
		{ /* ... */ }
	}
}
[Ignore]
Dočasně označí třídu nebo metodu, která je označena atributem [Test] nebo [TestFixture] tak, aby testy nebyly provedeny. Slouží k dočasnému odstranění testů. Parametr umožňuje zadat test zprávy, proč testy nejsou prováděny.
[Explicit]
Označený test je prováděn pouze pokud je explicitně zadán na příkazové řádce nebo v GUI. Pokud zadán není, je test ignorován, ale je o tom vypsána zpráva.

Práce s NUnit

Při vytváření testů musí mít třída atribut [TestFixture] a tento atribut může být zděděn. Třída musí být public a musí mít implicitní konstruktor. Metody v třídě, které mají provádět testy, musí mít atribut [Test] a nesmí očekávat žádné parametry a vracet žádnou hodnotu.

Při provádění testů spusťte program nunit-gui.exe a pomocí volby File → Open otevřete dll, který jste zkompilovali ve svém vývojovém prostředí. Pro testování NUnit frameworku můžete vyzkoušet nunit.tests.dll, který je dodáván včetně zdrojových kódů společně s NUnit. V levém panelu se zobrazí stromová struktura testů a vpravo je zobraz status panel. Kliknutím na tlačítko Run se spustí test. Pokud je v levém panelu vybrán kořen stromu, jsou spuštěny všechny testy, pokud ne, jsou spuštěny pouze vybrané testy.

V případě, že ve svém vývojovém prostředí změníte a překompilujete svůj kód, NUnit GUI ho automaticky načte znovu - není nutné mu oznamovat, že došlo k překompilování.

Jestliže je GUI program spuštěn bez argumentu, je automaticky načteno poslední assembly. Program ovšem není automaticky spuštěn, assembly je pouze načteno. GUI program si také automaticky pamatuje pět posledních načtených assembly. Tyto assembly jsou k dispozici pod volbou File → Recent Asseblies. Další možností je specifikovat assembly na příkazové řádce, například:

Hlavní okno GUI NUnit

nunit-gui.exe nunit.tests.dll

Program spouštěný z příkazového řádku má více voleb. Tento konzolový program vždy vytváří XML soubor, do kterého ukládá výsledek testů. Tento soubor se implicitně jmenuje TestResult.xml a je umístěn v pracovním adresáři. Při spuštění tohoto programu je vždy nutné uvést assembly, které má být testováno, například:

nunit-console nunit.tests.dll

Pokud nechcete provádět všechny testy, je možné použít argument /fixture:, který zajistí, aby byly provedeny pouze jím uvedené testy. V následujícím příkladu budou provedeny pouze testy NUnit.Tests.AssertionTests v assembly nunit.tests.dll.

nunit-console /fixture:NUnit.Tests.AssertionTests nunit.tests.dll

Argumentem /xml: lze změnit název souboru, do kterého budou uloženy výstupy z testů. Například takto lze uložit výstupy do souboru console-test.xml:

nunit-console /xml:console-test.xml nunit.tests.dll

Konzolový program používá pro zobrazení výstupu na obrazovku interní XSLT transformaci. Tuto lze změnit argumentem /transform: například podle vlastních transformačních pravidel uložených v souboru myTransform.xslt:

nunit-console /transform:myTransform.xslt nunit.tests.dll

Implicitní transformační soubor se jmenuje Summary.xslt a najdete ho v instalačním adresáři.

Parametr /wait vyžaduje stisknutí klávesy pro ukončení programu. Parametr /xmlconsole  provede XML výstup na obrazovku a ne do souboru. /nologo potlačí zobrazení informačního hlášení při startu programu a /help zobrazí nápovědu ke všem parametrům.

Kam psát testovací kód

Existují dvě varianty kam psát testovací kód. První varianta, která je vhodná pro menší nebo interní projekty je, psát testovací kód přímo do assembly, které obsahuje testované třídy. V případě větších projektů je výhodnější testovací kód umístit do samostatného assembly.

Pokud testovací kód umístíte mimo produkční kód, budete mít výsledný kód menší a bude v něm méně zmatku. Nicméně má to své nevýhody. Dva samostatné balíky, které spolu přímo souvisejí - produkční a testovací, to může být někdy trochu komplikované. Navíc jsou dobře psané testovací jednotky minimálně stejně dobré jako komentáře k zdrojovému kódu. Testovací jednotky totiž zcela zřetelně ukazují k čemu produkční kód slouží. Pokud je testovací a produkční kód pohromadě, jsou i pohromadě tyto cenné informace.

Dalším problémem s oddělením testovacích a produkčních jednotek je zapouzdření. Abychom třídy mohli testovat, musíme k nim mít přístup. Pokud je testovací jednotka umístěna ve stejném assembly a namespace jako produkční, nebývá se zapouzdřeném problém. Pokud je v jiném assembly, je nutné aby byla public. To ovšem odporuje principům zapouzdření.

Myslím tedy, že lepším řešením je testovací kód (třídy) umístit do stejného balíku, nebo dokonce souboru, jako třídu(y) produkční. Zde ovšem vznikne zásadní otázka: jak z release verze odstranit testovací kód? Pokud předáme odladěný výsledný program zákazníkovi, měl by být bez testovacího kódu. Jediné použitelné řešení je, jak mi poradil pan Martin Vobr z Rebexu, je testovací třídu umístit mezi příkazy podmíněné kompilace. Například pro konfiguraci Debug vytvořit kompilační konstantu TEST a celou testovací třídu uzavřít mezi příkazy kompilátoru #if a #endif. Pokud potom zkompilujete Release verzi, která nebude znát kompilační konstantu TEST, nebude se v release verzi testovací kód vyskytovat. Není to sice ideální řešení, ale v tuhle chvíli lepší neznám.

Závěr

NUnit je velmi kvalitní a užitečný nástroj. V současné verzi neprojevuje žádné problémy a je zcela stabilní. Za jedinou nevýhodu tohoto skvělého produktu lze považovat nedostatečnou a roztříštěnou dokumentaci. Pokud myslíte, že vám testování zvýší efektivitu vývoje software (já myslím že ano), tak je NUnit výbornou volbou.

1 komentářů:

Anonymní řekl(a)...

Existuje taky add-in do Visual Studia, super vec:
http://sourceforge.net/projects/nunitaddin/

HlR