Pisząc testy jednostkowe często musimy brać skądś testowe wartości, których wykorzystanie będziemy następnie weryfikować. Przy okazji ostatniego posta mieliśmy takie linijki:
1: string userName = "testUserName"; 2: string password = "testPassword";
Łatwo jednak sobie wyobrazić ile takich miejsc może pojawić się w skali całego projektu. Tysiące! I czy będzie się komuś chciało nadawać każdej zmiennej sensowną wartość? Wątpię. Z pewnością bardzo szybko da się zaobserwować takie zjawisko:
1: string userName = "asdawdczcasead"; 2: string password = "dq23rwfasdawdswsadfsd";
Fe.
Czytałem gdzieś kiedyś, że stosowanie wartości losowych w testach jednostkowych jest złe. Poniekąd się z tym zgadzam, jednak jak sobie z tym radzić? Przypisywanie coś_mówiących stringów mija się w tym przypadku z celem. A niekontrolowany rajd po klawiaturze to nic innego jak… wartość losowa! Tyle że jest jeszcze gorsza niż napis losowy wygenerowany przez przemyślany mechanizm działający według zdefiniowanych reguł.
Ja od dłuższego czasu przy testach jednostkowych korzystam z własnej klasy Randomizer, która generuje mi wartości o spełnionych kryteriach (jak napis o określonej długości, losowa data, liczba mieszcząca się w pewnych granicach itd). Dla typów bardziej skomplikowanych, wchodzących w skład domeny, można pokusić się o zastosowanie Object Mother, Test Data Builder czy czegoś w ten deseń, ale dla takich prostych scenariuszy poniższa klasa spełnia swoje zadanie. Enjoy, uwagi jak zwykle mile widziane:
1: public static class Randomizer 2: { 3: private static readonly Random _random = new Random(); 4: 5: #region String 6: 7: private const int A = 'A'; 8: private const int z = 'z'; 9: 10: /// <summary> 11: /// Generates a random string of a random length. 12: /// </summary> 13: public static string String() 14: { 15: return String(_random.Next(200)); 16: } 17: 18: /// <summary> 19: /// Generates a random string of a specified length. 20: /// </summary> 21: /// <param name="length">Length of the result string.</param> 22: public static string String(int length) 23: { 24: StringBuilder result = new StringBuilder(length); 25: 26: for (int i = 0; i < length; i++) 27: { 28: var charToAppend = (char)_random.Next(A, z + 1); 29: if (char.IsLetter(charToAppend) == false) 30: { 31: i--; 32: } 33: else 34: { 35: result.Append(charToAppend); 36: } 37: } 38: 39: return result.ToString(); 40: } 41: 42: #endregion 43: 44: #region Number 45: 46: public static int Number() 47: { 48: return _random.Next(); 49: } 50: 51: public static int Number(int min, int max) 52: { 53: return _random.Next(min, max); 54: } 55: 56: public static int Number(int max) 57: { 58: return _random.Next(max); 59: } 60: 61: public static int[] NumberArray() 62: { 63: return NumberArray(_random.Next(2, 15)); 64: } 65: 66: public static int[] NumberArray(int length) 67: { 68: int[] result = new int[length]; 69: 70: for (int i = 0; i < length; i++) 71: { 72: result[i] = Number(); 73: } 74: 75: return result; 76: } 77: 78: #endregion 79: 80: #region TimeSpan 81: 82: public static TimeSpan TimeSpan() 83: { 84: return new TimeSpan(Number()); 85: } 86: 87: #endregion 88: 89: #region Guid 90: 91: public static Guid Guid() 92: { 93: return System.Guid.NewGuid(); 94: } 95: 96: #endregion 97: 98: #region DateTime 99: 100: public static DateTime DateTime() 101: { 102: return new DateTime( 103: Number(2000, 2010), Number(1, 13), Number(1, 25), Number(0, 24), Number(0, 60), Number(0, 60) 104: ); 105: } 106: 107: #endregion 108: }
Do generowania bardziej skomplikowanych konstrukcji polecam NBuilder: http://nbuilder.org/
Jest to dużo wygodniejsze niż generowanie list za każdym razem za pomocą for’a :)
Strasznie fajnie wygląda, dzięki za info:)
Może też pomóc PEX (http://research.microsoft.com/en-us/projects/Pex/).
Pex jest wporzo, ale służy raczej do czegoś innego – on przecież generuje całe testy sprawdzające różne scenariusze wywoływania już istniejącego kodu, a nie proste wartości do skonfigurowania testowego środowiska dla jednego konkretnego testu.
Generować też może, ale w prostym użyciu to sprytnie dobierze parametry, w tym przypadku i, zamiast losowych wartości wybierze różne podejrzane…:
wybierze rozne podejrzane w tym \0 dla wartosci string. pytanie tylko… czy aby na pewno zawsze jest sens testowania wartosci \0 ? :)
A Pex swoja droga to fajna rzecz, ale jak % zauwazyl, jego wielki plus jest wtedy kiedy mamy juz gotowy kod.
Gutek
Poza tym Pex chyba nie jest darmowy do komercyjnego zastosowania.