IntelliTest to wewnętrzna funkcjonalność Visual Studio (w wersji Enterprise), która służy do generowania tabeli danych wejściowych oraz zestawu testów jednostkowych. Dla danej metody generowane są dane wejściowe, w oparciu których mogą zostać wygenerowane testy jednostkowe.
Przypadki testowe tworzone są w oparciu o analizę każdego skoku warunkowego (conditional branch). Co więcej, tabela przypadków testowych zawiera scenariusze, które nie zostały obsłużone w naszej logice biznesowej, a które zwracają wyjątek (np. NullReferenceException lub dzielenie przez zero).
IntelliTest współpracuje natywnie z MSTest oraz poprzez rozszerzenia z xUnit.net i NUnit.
Źródło screenshota: http://blogs.msdn.com/b/visualstudio/archive/2015/09/30/intellitest-for-net-test-more-with-less-effort.aspx
Zanim powstał IntelliTest, Microsoft stworzył i przemianował kilka innych narzędzi:
Pex (http://research.microsoft.com/en-us/projects/pex/) to dziecko Microsoft Research, które posłużyło jako prototyp IntelliTestu. Visual Studio 2010 Power Tools zawiera dwa narzędzia do testowania: Pex i Moles. Pex generuje tablicę wartości we-/wyjściowych i pozwala na automatyczne stworzenie testów jednostkowych. (Moles zmienił nazwę Fakes; jest narzędzie do izolacji kodu.)
Pex dostępny jest w wersji online na stronie: http://www.pexforfun.com/ i jako aplikcja dla Windows Phone.
Wersja online zawiera samouczek (w tym samouczek języka C#/VB/F#), quizy do rozwiązania i coding duel.
Code Digger (http://research.microsoft.com/en-us/projects/codedigger/) to narzędzie z silniczkiem Peksa służącym do generowania testów dla PCL (Portable Class Libraries).
Przed Release Candidate Visual Studio 2015, IntelliTest miało nazwę “Smart Unit Tests”.
Aby zobrazować działanie IntelliTest, posłużmy się algorytmem wyliczającym BMI. Nasz kod bazowy wygląda następująco:
public class BmiCalculator
{
public BmiKind Calculate(int massInKg, int heightInCentimetres)
{
decimal heightInMetres = heightInCentimetres / 100m;
decimal bmi = massInKg / (heightInMetres * heightInMetres);
if (bmi < 18.5m) return BmiKind.Underweight;
if (bmi < 25) return BmiKind.Normal;
if (bmi < 30) return BmiKind.Overweight;
if (bmi >= 30) return BmiKind.Obese;
throw new InvalidOperationException();
}
}
public enum BmiKind
{
Underweight = 0,
Normal = 1,
Overweight = 2,
Obese = 3
}
W menu kontekstowym Visual Studio widnieją dwie opcje dla IntelliTest: Run i Create IntelliTest.
Analiza przypadków dla danej metody/klasy, które otwiera okno “IntelliTest Exploration Results”:
Powyższa analiza została uruchomiona na naszym kodzie wyliczającym indeks BMI. (Jako że IntelliTest gorzej radzi sobie z liczbami zmiennoprzecinkowymi, wzrost osoby podawana jest w centrymetrach, typ tej zmiennej to int.) W naszym przypadku analiza poradziła sobie bezproblemowo z wszystkimi przypadkami testowymi. Udało się pokryć logiczną zawartość naszej metody. IntelliTest wygenerował zestaw wartości, który zwraca każdą wartość enum BmiKind
. Uwagi:
DivideByZeroException
.InvalidOperationException
w ostatniej linii metody Calculate
nie zostało pokryte, gdyż ten kod jest nieosiągalny.Zobaczmy co się stanie jeśli wprowadzimy ograniczenia co zmiennych okreś. Wprowadźmy na początek metody warunki początkowe:
const int MinMass = 40; // kg
const int maxMass = 635; // kg
if (massInKg <= MinMass || massInKg > maxMass)
throw new ArgumentOutOfRangeException();
const int minHeight = 50; // cm
const int maxHeight = 272; // cm
if (heightInCentimetres <= minHeight || heightInCentimetres > maxHeight)
throw new ArgumentOutOfRangeException();
W wyniku otrzymamy następującą tabelę wartości we-/wyjścia:
W dalszym ciągu zestaw wartości wejściowych pokrywa się z każdą wartością enum BmiKind. Obsłużony został także przypadek dzielenia przez zero, stąd IntelliTest nie zwraca nam uwagi na ten problem.
Druga opcja to Create IntelliTest – Stworzenie automatycznych testów jednostkowych:
Przykładowy kod wygenerowany przez IntelliTest dla wartości wejściowych: masa = 41 kg, wzrost = 51 cm wraz z oczekiwanym rezultatem = BmiKind.Obese
wygląda następująco:
[TestMethod]
[PexGeneratedBy(typeof(BmiCalculatorTest))]
public void Calculate186()
{
BmiKind i;
global::BmiCalculator.BmiCalculator s0
= new global::BmiCalculator.BmiCalculator();
i = this.Calculate(s0, 41, 51);
Assert.AreEqual<BmiKind>(BmiKind.Obese, i);
Assert.IsNotNull((object)s0);
}
Wnioski Do czego w praktyce może przydać się IntelliTest? Można wyróżnić dwie dziedziny:
Przykład BMI obrazuje zachowanie IntelliTest dla dwóch parametrów wejściowych, ktróre są liczbą całkowitą. IntelliTest radzi sobie oczywiście też z innymi przypadkami, m.in. liczbami zmiennoprzecinkowymi, obiektami, pętlami, wyrażeniami regularnymi. Dla chcących sprawdzić sposób zachowania, polecam odwiedzenie wyżej wspomnianego Pex for Fun: http://pexforfun.com/.