Bardziej praktyczne podejście do odpowiedzi pdr . TDD polega raczej na projektowaniu oprogramowania niż na testowaniu. Testy jednostkowe służą do weryfikacji pracy w miarę upływu czasu.
Tak więc na poziomie testu jednostkowego musisz zaprojektować jednostki, aby mogły być testowane w całkowicie deterministyczny sposób. Możesz to zrobić, usuwając wszystko, co sprawia, że jednostka nie jest deterministyczna (np. Generator liczb losowych), i usuwając to. Powiedzmy, że mamy naiwny przykład metody decydującej, czy ruch jest dobry, czy nie:
class Decider {
public boolean decide(float input, float risk) {
float inputRand = Math.random();
if (inputRand > input) {
float riskRand = Math.random();
}
return false;
}
}
// The usage:
Decider d = new Decider();
d.decide(0.1337f, 0.1337f);
Ta metoda jest bardzo trudna do przetestowania, a jedyne, co naprawdę można zweryfikować w testach jednostkowych, to jej granice ... ale to wymaga wielu prób dotarcia do granic. Zamiast tego streśćmy losową część, tworząc interfejs i konkretną klasę, która otacza funkcjonalność:
public interface IRandom {
public float random();
}
public class ConcreteRandom implements IRandom {
public float random() {
return Math.random();
}
}
Decider
Klasa musi teraz korzystać z betonu klasy poprzez abstrakcję, czyli interfejsu. Ten sposób robienia rzeczy nazywa się wstrzykiwaniem zależności (poniższy przykład jest przykładem wstrzykiwania konstruktora, ale można to również zrobić za pomocą settera):
class Decider {
IRandom irandom;
public Decider(IRandom irandom) { // constructor injection
this.irandom = irandom;
}
public boolean decide(float input, float risk) {
float inputRand = irandom.random();
if (inputRand > input) {
float riskRand = irandom.random();
}
return false;
}
}
// The usage:
Decider d = new Decider(new ConcreteRandom);
d.decide(0.1337f, 0.1337f);
Możesz zadać sobie pytanie, dlaczego ten „nadęty kod” jest konieczny. Cóż, na początek, możesz teraz kpić z zachowania losowej części algorytmu, ponieważ Decider
teraz ma zależność, która następuje po IRandom
„kontrakcie”. Możesz użyć do tego frameworku, ale ten przykład jest wystarczająco prosty do samodzielnego kodowania:
class MockedRandom() implements IRandom {
public List<Float> floats = new ArrayList<Float>();
int pos;
public void addFloat(float f) {
floats.add(f);
}
public float random() {
float out = floats.get(pos);
if (pos != floats.size()) {
pos++;
}
return out;
}
}
Najlepsze jest to, że może to całkowicie zastąpić „faktyczne” konkretne wdrożenie. Kod można łatwo przetestować w następujący sposób:
@Before void setUp() {
MockedRandom mRandom = new MockedRandom();
Decider decider = new Decider(mRandom);
}
@Test
public void testDecisionWithLowInput_ShouldGiveFalse() {
mRandom.addFloat(0f);
assertFalse(decider.decide(0.1337f, 0.1337f));
}
@Test
public void testDecisionWithHighInputRandButLowRiskRand_ShouldGiveFalse() {
mRandom.addFloat(1f);
mRandom.addFloat(0f);
assertFalse(decider.decide(0.1337f, 0.1337f));
}
@Test
public void testDecisionWithHighInputRandAndHighRiskRand_ShouldGiveTrue() {
mRandom.addFloat(1f);
mRandom.addFloat(1f);
assertTrue(decider.decide(0.1337f, 0.1337f));
}
Mam nadzieję, że daje to pomysły na zaprojektowanie aplikacji, dzięki czemu można wymusić permutacje, aby można było przetestować wszystkie przypadki brzegowe i tak dalej.