Unit testy mají být krátké, čtivé a přehledné. Jak toho ale docílíme pokud potřebujeme vytvořit mnoho objektů. Řešením je tzv. Test Utility Code.
Co je Test Utility Code?
Test Utility Code je seskupení pomocných tříd ke zpřehlednění samotného testu. Metoda testu by měla obsahovat pár řádků kódu, ze kterého bude na první pohled jasné, co metoda testuje(Act), co k testu potřebujeme(Arrange) a jak se test vyhodnocuje(Assert). Největší část pomocných tříd se bude týkat tvorby objektů či primitivních typů potřebných ke správnému naaranžování situace, kterou chceme testovat. Slovo “Naaranžování” je zde použito schválně, aby jste poznali, že mluvíme o části metody Unit Testu, kterou nazýváme Arrange.
Tvorba objektů pro Unit Test
Velmi silně doporučuji mít pouze jedno jediné místo v celém projektu pro Vaše unit testy, kde budete vytvářet objekt určité třídy. Pokud budete konstruktoru takové třídy v budoucnosti přidávat další parametr, tak budete muset upravit pouze jedno místo v celém projektu s testy. Pokud budete vytvářet objekt pomocí klíčového slova NEW v každém testu, kde je objekt potřebný, budete muset všude doplnit nový parametr.
Test Data Builder
Pokud chcete testovat výstup, často potřebujete vstup, tedy Data. Mým oblíbeným nástrojem pro tvorbu kolekcí dat je Nbuilder. NBuilder je knihovna, která Vám umí velmi šikovně vygenerovat kolekci potřebného typu.
Builder<Author>
.CreateListOfSize(30)
.Build();
Obsah jednotlivých Properties třídy je možné ovlivnit například pomocí metod With.
Builder<Author>
.CreateListOfSize(30)
.All()
.With(x => x.Name = "Daniel")
.With(x => x.Surname = "Rusnok")
.Build();
Je možné také využít RandomGenerator, který najdete také v knihovně NBuilder.
Builder<Author>
.CreateListOfSize(30)
.All()
.With(x => x.Id = new RandomGenerator().Next(1, 3))
.Build();
Pokud nechcete využít NBuilder, vytvořte si vlastní Builder své třídy pomocí Fluent Builder Pattern.
Fluent Builder Pattern
Síla Fluent Builder Pattern je v použití různých variací testovacích dat. Pokud chcete vytvořit instanci Author bez jména a příjmení, jednoduše zavoláte metodu Build.
var author = new AuthorBuilder().Build();
Pokud potřebujete pro svůj test objekt Author pouze se jménem, zavoláte danou metodu WithName, a jméno předáte.
var author = new AuthorBuilder().WithName("Daniel").Build();
Pokud potřebujete pro svůj test objekt se jménem, příjmením a věkem.
var author = new AuthorBuilder().WithName("Daniel").WithSurname("Rusnok").WithAge("18").Build();
Řekněme, že nechceme pokaždé volat tři metody With. Poté je možné vytvořit metodu, která rovnou nastaví objektu stav, ve kterém ho chceme.
var author = new AuthorBuilder().WithValidDeliveryAddress().Build();
Třída AuthorBuilder by poté mohla vypadat takto:
public class AuthorBuilder
{
private string name;
private string surname;
private int age;
private string street;
private string zipcode;
private string city;
public AuthorBuilder WithName(string name)
{
this.name = name;
return this;
}
public AuthorBuilder WithSurname(string surname)
{
this.surname = surname;
return this;
}
public AuthorBuilder WithAge(int age)
{
this.age = age;
return this;
}
public AuthorBuilder WithValidDeliveryAddress()
{
this.street = "28. října 1724/8";
this.zipcode = "123 85";
this.city = "Ostrava";
return this;
}
public Author Build()
{
return new Author(name, surname, age, street, zipcode, city);
}
}
Závěr
Ano, jedná se o více práce okolo, ale díky Fluent Builder Pattern se Vám příjemně zpřehlední testy. Čím jsou testy přehlednější, tím máme větší šanci rychleji testu porozumět a identifikovat, proč takový test neprochází.