fbpx
devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
4 minut

Generator wartości dla testów jednostkowych


20.07.2009

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:  }
0 0 votes
Article Rating
7 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
matma
matma
14 years ago

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 :)

procent
14 years ago

Strasznie fajnie wygląda, dzięki za info:)

bezieur
bezieur
14 years ago
procent
14 years ago

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.

bezieur
bezieur
14 years ago

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…:

using System;
using Microsoft.Pex.Framework;
[PexClass]
public partial class TestClass {
[PexMethod]
public void ParameterizedTest(int i) {
if (i == 123)
throw new ArgumentException("i");
}
}
Gutek
14 years ago

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

jj
jj
14 years ago

Poza tym Pex chyba nie jest darmowy do komercyjnego zastosowania.

Kurs Gita

Zaawansowany frontend

Szkolenie z Testów

Szkolenie z baz danych

Książka

Zobacz również