komponententests und testabdeckung
DESCRIPTION
Komponententests und TestabdeckungTRANSCRIPT
Komponententests und Testabdeckung
Christian Baranowski���1
Komponenten Test
• Testbasis (Was ist die Grundlage für die Testfälle) • Anforderungen an die Komponente • detaillierter Entwurf • Code
• Testobjekte (SUT = System Under Test): • Komponenten • Programme • Datenumwandlung/Migrationsprogramme • Datenbankmodule
���2
Vier Phasen Test (Four-Phase Test)
Setup
Verify
Teardown
Exercise
SUT
Fixture1
2
3
4
���3
Quelle: XUnit Test Patterns - Gerard Meszaros
JUnit 3.X Design
+ run(TestResult)
«interface»Test
+ run(TestResult)+ runTest()+ setUp()+ tearDown()
TestCase+ run(TestResult)+ addTest(Test)
TestSuite
+ setUp()+ testMyTest()+ tearDown()
MyTest
TestResult
Assert
���4
JUnit 3 Examplepublic class CalcTest extends TestCase { ! Calc calc; protected void setUp() throws Exception { calc = new Calc(); } ! protected void tearDown() throws Exception { calc = null; } ! public void testSum() { int sum = calc.sum(); assertEquals(0, sum); } !}
���5
JUnit 4.X Design+ getDescription()
«interface»Describable
Description
+ getDescription()+ run(RunNotifier)
Runner
+ getDescription()+ run(RunNotifier)
ParentRunner
+ filter(Filter)
«interface»Filterable
+ sort(Sorter)
«interface»Sortable
TestClass
BlockJUnit4ClassRunner
+ setup()+ myTest()+ teardown()
MyTest
Assert
«interface»@Before
«interface»@Test
«interface»@After
«run»
«static import» «use»
���6
JUnit 4 Exampleimport static org.junit.Assert.*; import org.junit.*; !public class CalcTest { Calc calc; @Before public void setup() { calc = new Calc(); } @Test public void sum() { assertEquals(0, calc.sum()); } @After public void teardown() { calc = null; } }
���7
JUnit3 Test Exceptions
try { sut.sort(null); fail("NPE was expected!"); } catch(NullPointerException exp){ assertTrue(true); }
���8
JUnit4 Test Exceptions
@Test(expected = NullPointerException.class) public void sort_NullPointerException() { sut.sort(null); }
���9
JUnit4 Test Exceptions
@Rule public ExpectedException thrown= ExpectedException.none(); @Test public void testSort_NullPointerException() { thrown.expect(NullPointerException.class); thrown.expectMessage("The values should not be null."); sut.sort(null); }
���10
JUnit4 Rulespublic class IgnoreRule implements MethodRule {! @Retention(RetentionPolicy.RUNTIME) public static @interface IgnoreTest {}! @Override public Statement apply(final Statement base,
final FrameworkMethod method, Object target) { return new Statement() { @Override public void evaluate() throws Throwable { if (method.getAnnotation(IgnoreTest.class) == null) { base.evaluate(); } } }; }}
���11
JUnit4 Ignore Rule Test
@Rule public IgnoreRule ignoreRule = new IgnoreRule(); @Test @IgnoreTest public void failingTest() { throw new RuntimeException("Not Implement"); }
���12
JUnit4 Runner (Parametrisierter Test)@RunWith(Parameterized.class) public class SorterTest { ! @Parameters public static Iterable<Object[]> data() { return Arrays.asList(new Object[][] {{ new Integer[] { 5, 4, 3 }, new Integer[] { 3, 4, 5 } }}); } ! public SorterTest(Integer[] values, Integer[] expected) { this.values = values; this.expectedValues = expected; }
���13
Strukturorientierter Test
���14
Strukturorientierter Test (White-Box Verfahren)
Komponentenebene • Struktur der Softwarekomponente • Anweisungen • Entscheidungen • Zweige oder einzelne Pfade
Integrationsebene • Aufrufgraph oder Sequenzdiagramm
���15
„Als Testabdeckung bezeichnet man das Verhältnis an tatsächlich getroffenen Aussagen eines Tests gegenüber den theoretisch möglich treffbaren Aussagen bzw. der Menge der gewünschten treffbaren Aussagen.“ - Wikipedia
Testabdeckung
���16
Testabdeckungboolean aAndbOrC(boolean a, boolean b, boolean c) { return(a && b) || c;}
Erwartet a b c
false false false false
true false false true
false false true false
true false true true
false true false false
true true false true
true true true false
true true true true
���17
Vollständige Testabdeckung
„Eine vollständige Testabdeckung stellt eine Ausnahme dar, weil die Anzahl möglicher Testfälle sehr schnell ungeheuer groß wird (durch kombinatorische Explosion). Ein vollständiger Funktionstest für eine einfache Funktion, die zwei 16-Bit-Werte als Argument erhält, würde schon 2^(16+16), also ca. 4 Milliarden Testfälle bedeuten, um die Spezifikation vollständig zu testen.“ - Wikipedia
���18
Flussdiagramm (flowchart)
boolean aAndbOrC( boolean a, boolean b, boolean c) { boolean result = false; if(A && B) { result = true; } else if(C) { result = true; } else { result = false; } return result; }
result = false
Start
return result
A & B result = true
C result = true
result = false
���19
Vereinfachtes Flussdiagramm
boolean aAndbOrC( boolean a, boolean b, boolean c) { boolean result = false; if(a && b) { result = true; } else if(c) { result = true; } else { result = false; } return result;}
a && b
c
false
true
true
���20
Anweisungsüberdeckung (Statement Coverage)
a && b
c
false
true
true Erwartet a b c
true true true false
���21
Anweisungsüberdeckung (Statement Coverage)
a && b
c
false
true
true Erwartet a b c
true true true false
true false false true
���22
Anweisungsüberdeckung (Statement Coverage)
a && b
c
false
true
true Erwartet a b c
true true true false
true false false true
false false true false
100 % Anweisungsüberdeckung
���23
Testabdeckung
int sum(int values[], int offset){ int result = 0; for (int value : values) { if(offset > 0){ if(offset < value) result += offset; } result += value; } return result; }
���24
Testabdeckung
result = 0
for(value : values)
offset > 0 offset < value
result += offset
result += value
return result
Erwartet values offset
3 [2] 1
100 %Anweisungs- überdeckung
���25
Zweigabdeckung (Branch Coverage)
result = 0
for(value : values)
offset > 0 offset < value
result += offset
result += value
return result
Erwartet values offset
3 [2] 1
���26
Zweigabdeckung (Branch Coverage)
result = 0
for(value : values)
offset > 0 offset < value
result += offset
result += value
return result
Erwartet values offset
3 [2] 1
2 [2] 0
���27
Zweigabdeckung (Branch Coverage)
result = 0
for(value : values)
offset > 0 offset < value
result += offset
result += value
return result
Erwartet values offset
3 [2] 1
2 [2] 0
2 [2] 2
Merke: 100% Zweigabdeckung schließt 100% Anweisungsüberdeckung ein!!!
���28
Entscheidungsüberdeckung (Term Coverage)boolean aAndbOrC( boolean a, boolean b, boolean c) { if((a && b) || c) { return true; } else { return false; }}
Erwartet a b c
true true true false
true false false true
false false true false
Merke: 100% Entscheidungsüberdeckung schließt 100% Zweigabdeckung ein!!!
���29
Überblick kontrollflussorientierten Testverfahren (Überdeckungstests)
���30
Anweisungsüberdeckung
int result = 0
return result
each(values)
result += offset
offset > 0
result += value
offset < value
���31
Zweigabdeckung
int result = 0
return result
each(values)
result += offset
offset > 0
result += value
offset < value
���32
Entscheidungsüberdeckungint result = 0
return result
each(values)
result += offset
offset > 0
result += value
offset < value
���33
Schleifenüberdeckung (Loop Coverage)
int result = 0
return result
each(values)
result += offset
offset > 0
result += value
offset < value
Schleife wird:- keinmal- genau einmal- mehr als einmaldurchlaufen
���34
Pfadüberdeckungstest (Path Coverage)
Pfadüberdeckungstest werden alle möglichen Pfade betrachtet. !Pfade in Schleifen: n = Anzahl Schleifendurchläufe m = Anzahl der Pfade in der Schleife P = m(n)+ m(n-1)+ m(n-2) + … + m0 !Beispiel: max(values) = 5 P = 35 + 34 + 33 + 32 + 31 + 1 = 364 243 + 81 + 27 + 9 + 3 + 1 = 364 !Bei Schleifen ergeben sich sehr viele Pfade ⇒ kombinatorische Explosion.
A
for(values)
B D
EC
F
���35
Pfadüberdeckungstest
A
for(values)
B
C
F
A
for(values)
B D
C
F
A
for(values)
B D
EC
F
max(values) = 5 max(values) = 4 max(values) = 6
���36
P = 15+14+13+12+11+10
P = 6
m = 1
P = 24+23+22+21+20
P = 31
m = 2
P = 36+35+34+33+32+31+30
P =1093
m = 3
Pfadüberdeckungstest
def n = 5 def loopPaths = ['BC', 'BDC', ‚BDEC'] !def pathCount = 1 def continuous = [] def pathTestCases = [] (1..n).each { continuous.add(Collections.nCopies( it, loopPaths )) } continuous.each { def combinations = it.combinations() def paths = combinations.size() pathCount += paths pathTestCases.addAll(combinations) } pathTestCases.eachWithIndex { test, nr -> println "${nr + 1} \t ${test}" }
Algorithmus Loop Paths (Groovy):
���37
Komponententests für echte Klassen
���38
Testen ohne Abhängigkeiten
«component»SUT
«component»DOC
depended-on component
*
Test
*
���39
Testen ohne Abhängigkeitenpublic class Calc { ! private int result = 0; ! private Cache cache = new Cache(); ! public void add(int a) { if (a < 0) throw new IllegalStateException(); String stmt = String.format("%d + %d", result, a); Integer cachedResult = cache.get(stmt); if (cachedResult != null) result = cachedResult; else cache.put(stmt, result += a); } ! public int sum() { return result; } }
���40
Testability (Prüfbarkeit)
public class Calc { ! private int result = 0; ! // DOC Cache cache = new Cache(); …
// DOC private Cache cache = new Cache(); public void setCache(Cache cache) { this.cache = cache; }
Package Visible Dependencies
Setters for Dependencies
���41
Test Doubles
���42
Test Doubles
Test Double
Fake Object
Test Stub
Mock Object
Test Spy
DummyObject
���43
Quelle: XUnit Test Patterns - Gerard Meszaros
Test Stub
Setup
Verify
Teardown
Exercise
Stub !
!
Create
Install
Return Values
Indirect Inputs
SUT
���44
Quelle: XUnit Test Patterns - Gerard Meszaros
Stubbing with Mockitoclass Calc { Cache cache; int plus(int a, int b) { Object cachedValue = cache.get(a + "+" + b); if(cachedValue != null) return (Integer) cachedValue; return a + b; } }!@Testpublic void plus() { when(stubCache.get("1+1")).thenReturn(3); int result = calc.plus(1, 1); assertEquals(3, result);}
���45
Setup
Verify
Teardown
Exercise
Mock Object
!
!
!
Create
Install
ExpectionsIndirect InputsSUT
RecordVerify
Mock Object
���46
Quelle: XUnit Test Patterns - Gerard Meszaros
Mocking with Mockitoclass EventAdmin { List<EventListener> listeners = new ArrayList<>(); public void subscribe(EventListener listener) { listeners.add(listener); } public void send(String event, Map<?, ?> properties) { for (EventListener listener : listeners) listener.onEvent(event, properties); }}!@Test public void send() { EventListener mockListener = mock(EventListener.class); eventAdmin.subscribe(mockListener); eventAdmin.send("ups", null); verify(mockListener).onEvent("ups", null);}
���47
Test Spy
Setup
Verify
Teardown
Exercise
Test Spy !
!
!
Create
Install
Indirect InputsSUT
Record
Verify
���48
Quelle: XUnit Test Patterns - Gerard Meszaros
Spy Objects with Mockito
@Test public void send() { EventListener listener = new EventListener() { @Override public void onEvent(String e, Map<?, ?> p) {} }; EventListener mockListener = spy(listener); eventAdmin.subscribe(mockListener); eventAdmin.send("ups", null); verify(mockListener).onEvent("ups", null); }
���49