More Related Content Similar to Executable documentation (20) Executable documentation2. Any fool can write code that a
computer can understand. Good
programmers write code that humans
can understand.
- Martin Fowler
3. What’s wrong with this test?
@Test
public void testRead() throws Exception {
Mockito.when(eofwatcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(instream.read()).thenReturn(0, -1);
Assert.assertEquals(0, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNotNull(eofstream.getWrappedStream());
Mockito.verify(eofwatcher, Mockito.never()).eofDetected(instream);
Assert.assertEquals(-1, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNull(eofstream.getWrappedStream());
Mockito.verify(instream, Mockito.times(1)).close();
Mockito.verify(eofwatcher).eofDetected(instream);
Assert.assertEquals(-1, eofstream.read());
}
(from the open-source project HttpClient)
4. Value of Unit Tests
Prevent Regression
Drive Development Facilitate Refactoring
Document Functionality
5. Strive for Clean Code
if ((getDate().year – dob.year > 62 || (getDate().year – dob.year == 62
&& getDate().month > dob.month)) && early)
payment = getEarlyPay();
else if ((getDate().year – dob.year > 65 || (getDate().year – dob.year == 65
&& getDate().month >= dob.month)) && !early)
payment = getRegPay();
else
payment = 0;
Duplication
6. Strive for Clean Code
if (getAgeInYears() >= 62 && early)
payment = getEarlyPay();
else if (getAgeInYears() >= 65 && !early)
payment = getRegPay();
else
payment = 0;
int getAgeInYears() {
int age = getDate().year – dob.year;
if (getDate().month >= dob.month) age++;
return age;
}
Magic
numbers
7. Strive for Clean Code
final static int EARLY_RETIREMENT_AGE = 62;
final static int REGULAR_RETIREMENT_AGE = 65;
if (getAgeInYears() >= EARLY_RETIREMENT_AGE && early)
payment = getEarlyPay();
else if (getAgeInYears() >= REGULAR_RETIREMENT_AGE && !early)
payment = getRegPay();
else
payment = 0; Complex conditional
8. Strive for Clean Code
final static int EARLY_RETIREMENT_AGE = 62;
final static int REGULAR_RETIREMENT_AGE = 65;
if (isCollectingEarlyRetirement())
payment = getEarlyPay();
else if (isCollectingRegularRetirement())
payment = getRegPay();
else
payment = 0;
boolean isCollectingEarlyRetirement() {
return early && getAgeInYears() >= EARLY_RETIREMENT_AGE);
}
Ambiguous
names
9. Strive for Clean Code
final static int EARLY_RETIREMENT_AGE = 62;
final static int REGULAR_RETIREMENT_AGE = 65;
if (isCollectingEarlyRetirement())
payment = getEarlyRetirementPayment();
else if (isCollectingRegularRetirement())
payment = getRegularRetirementPayment();
else
payment = 0;
boolean isCollectingEarlyRetirement() {
return electedEarlyRetirement && getAgeInYears() >= EARLY_RETIREMENT_AGE);
}
10. Unit Tests are Code
Choose good method names
Keep methods short and coherent
Avoid magic numbers
Avoid long argument lists
Avoid duplication
11. Test One Thing Per Test
@Test
public void afterCellAdded_boardIsAtLeast10x10() {
Board board = new Board();
board.addCell(0, 0);
assertThat(board.getHeight(), is(greaterThan(10));
assertThat(board.getWidth(), is(greaterThan(10));
}
Name describes exactly what is being tested
Set up (could be part of test class setup)
Operation to test
Validation
13. What’s wrong with this test?
@Test
public void testRead() throws Exception {
Mockito.when(eofwatcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(instream.read()).thenReturn(0, -1);
Assert.assertEquals(0, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNotNull(eofstream.getWrappedStream());
Mockito.verify(eofwatcher, Mockito.never()).eofDetected(instream);
Assert.assertEquals(-1, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNull(eofstream.getWrappedStream());
Mockito.verify(instream, Mockito.times(1)).close();
Mockito.verify(eofwatcher).eofDetected(instream);
Assert.assertEquals(-1, eofstream.read());
}
14. @Test
public void testRead() throws Exception {
Mockito.when(eofwatcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(instream.read()).thenReturn(0, -1);
Assert.assertEquals(0, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNotNull(eofstream.getWrappedStream());
Mockito.verify(eofwatcher, Mockito.never()).eofDetected(instream);
Assert.assertEquals(-1, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNull(eofstream.getWrappedStream());
Mockito.verify(instream, Mockito.times(1)).close();
Mockito.verify(eofwatcher).eofDetected(instream);
Assert.assertEquals(-1, eofstream.read());
}
Ambiguous
Sequence of cases
Magic numbers
Confusing Names
16. Step 1: fix names
@Test
public void testRead() throws Exception {
Mockito.when(eofwatcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(instream.read()).thenReturn(0, -1);
Assert.assertEquals(0, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNotNull(eofstream.getWrappedStream());
Mockito.verify(eofwatcher, Mockito.never()).eofDetected(instream);
Assert.assertEquals(-1, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNull(eofstream.getWrappedStream());
Mockito.verify(instream, Mockito.times(1)).close();
Mockito.verify(eofwatcher).eofDetected(instream);
Assert.assertEquals(-1, eofstream.read());
}
17. Step 1: fix names
@Test
public void testRead() throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(0, -1);
Assert.assertEquals(0, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNotNull(eofstream.getWrappedStream());
Mockito.verify(watcher, Mockito.never()).eofDetected(wrappedStream);
Assert.assertEquals(-1, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNull(eofstream.getWrappedStream());
Mockito.verify(wrappedStream, Mockito.times(1)).close();
Mockito.verify(watcher).eofDetected(wrappedStream);
Assert.assertEquals(-1, eofstream.read());
}
18. Step 2: magic numbers
@Test
public void testRead() throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(0, -1);
Assert.assertEquals(0, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNotNull(eofstream.getWrappedStream());
Mockito.verify(watcher, Mockito.never()).eofDetected(wrappedStream);
Assert.assertEquals(-1, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNull(eofstream.getWrappedStream());
Mockito.verify(wrappedStream, Mockito.times(1)).close();
Mockito.verify(watcher).eofDetected(wrappedStream);
Assert.assertEquals(-1, eofstream.read());
}
19. Step 2: magic numbers
@Test
public void testRead() throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(0, -1);
Assert.assertEquals(0, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNotNull(eofstream.getWrappedStream());
Mockito.verify(watcher, Mockito.never()).eofDetected(wrappedStream);
Assert.assertEquals(-1, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNull(eofstream.getWrappedStream());
Mockito.verify(wrappedStream, Mockito.times(1)).close();
Mockito.verify(watcher).eofDetected(wrappedStream);
Assert.assertEquals(-1, eofstream.read());
}
20. Step 2: magic numbers
@Test
public void testRead() throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(DATA_VALUE, EOF);
Assert.assertEquals(DATA_VALUE, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNotNull(eofstream.getWrappedStream());
Mockito.verify(watcher, Mockito.never()).eofDetected(wrappedStream);
Assert.assertEquals(EOF, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNull(eofstream.getWrappedStream());
Mockito.verify(wrappedStream, Mockito.times(1)).close();
Mockito.verify(watcher).eofDetected(wrappedStream);
Assert.assertEquals(EOF, eofstream.read());
}
21. Step 3: What’s being
tested?@Test
public void testRead() throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(DATA_VALUE, EOF);
Assert.assertEquals(DATA_VALUE, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNotNull(eofstream.getWrappedStream());
Mockito.verify(watcher, Mockito.never()).eofDetected(wrappedStream);
Assert.assertEquals(EOF, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNull(eofstream.getWrappedStream());
Mockito.verify(wrappedStream, Mockito.times(1)).close();
Mockito.verify(watcher).eofDetected(wrappedStream);
Assert.assertEquals(EOF, eofstream.read());
}
22. Step 4: Separate the Tests
@Test
public void testRead() throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(DATA_VALUE, EOF);
Assert.assertEquals(DATA_VALUE, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNotNull(eofstream.getWrappedStream());
Mockito.verify(watcher, Mockito.never()).eofDetected(wrappedStream);
Assert.assertEquals(EOF, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNull(eofstream.getWrappedStream());
Mockito.verify(wrappedStream, Mockito.times(1)).close();
Mockito.verify(watcher).eofDetected(wrappedStream);
Assert.assertEquals(EOF, eofstream.read());
}
23. Step 4: Separate the Tests
@Test
public void whenNotAtEOF_readReturnsNextByte() throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(DATA_VALUE, EOF);
Assert.assertEquals(DATA_VALUE, eofstream.read());
}
24. Step 4: Separate the Tests
@Test
public void whenNotAtEOF_readReturnsNextByte() throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(DATA_VALUE, EOF);
Assert.assertEquals(DATA_VALUE, eofstream.read());
}
25. Step 4: Separate the Tests
@Test
public void whenNotAtEOF_readReturnsNextByteFromWrappedStream() throws Exception {
wrappedStream.returnOnRead(DATA_VALUE);
Assert.assertEquals(DATA_VALUE, eofstream.read());
}
26. Step 4: Separate the Tests
@Test
public void whenNotAtEOF_readReturnsNextByteFromWrappedStream() throws Exception {
wrappedStream.returnOnRead(DATA_VALUE);
Assert.assertEquals(DATA_VALUE, eofstream.read());
}
27. Step 4: Separate the Tests
@Test
public void whenNotAtEOF_readReturnsNextByteFromWrappedStream() throws Exception {
wrappedStream.returnOnRead(DATA_VALUE);
assertThat(eofstream.read(), is(DATA_VALUE));
}
28. Step 4: Separate the Tests
@Test
public void testRead() throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(DATA_VALUE, EOF);
Assert.assertEquals(DATA_VALUE, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNotNull(eofstream.getWrappedStream());
Mockito.verify(watcher, Mockito.never()).eofDetected(wrappedStream);
Assert.assertEquals(EOF, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
Assert.assertNull(eofstream.getWrappedStream());
Mockito.verify(wrappedStream, Mockito.times(1)).close();
Mockito.verify(watcher).eofDetected(wrappedStream);
Assert.assertEquals(EOF, eofstream.read());
}
29. Step 4: Separate the Tests
@Test
public void whenNotAtEOF_readDoesNotCloseStream () throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(DATA_VALUE, EOF);
Assert.assertEquals(DATA_VALUE, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
}
@Test
public void whenNotAtEOF_readReturnsNextByteFromWrappedStream() throws Exception {
wrappedStream.returnOnRead(DATA_VALUE);
assertThat(eofstream.read(), is(DATA_VALUE));
}
30. Step 4: Separate the Tests
@Test
public void whenNotAtEOF_readDoesNotCloseStream () throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(DATA_VALUE, EOF);
Assert.assertEquals(DATA_VALUE, eofstream.read());
Assert.assertFalse(eofstream.isSelfClosed());
}
@Test
public void whenNotAtEOF_readReturnsNextByteFromWrappedStream() throws Exception {
wrappedStream.returnOnRead(DATA_VALUE);
assertThat(eofstream.read(), is(DATA_VALUE));
}
31. Step 4: Separate the Tests
@Test
public void whenNotAtEOF_readDoesNotCloseStream () throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(DATA_VALUE, EOF);
eofstream.read();
Assert.assertFalse(eofstream.isSelfClosed());
}
@Test
public void whenNotAtEOF_readReturnsNextByteFromWrappedStream() throws Exception {
wrappedStream.returnOnRead(DATA_VALUE);
assertThat(eofstream.read(), is(DATA_VALUE));
}
32. Step 4: Separate the Tests
@Test
public void whenNotAtEOF_readDoesNotCloseStream () throws Exception {
Mockito.when(watcher.eofDetected(Mockito.<InputStream>any())).thenReturn(Boolean.TRUE);
Mockito.when(wrappedStream.read()).thenReturn(DATA_VALUE, EOF);
eofstream.read();
Assert.assertFalse(eofstream.isSelfClosed());
}
@Test
public void whenNotAtEOF_readReturnsNextByteFromWrappedStream() throws Exception {
wrappedStream.returnOnRead(DATA_VALUE);
assertThat(eofstream.read(), is(DATA_VALUE));
}
33. Step 4: Separate the Tests
@Test
public void whenNotAtEOF_readDoesNotCloseStream () throws Exception {
wrappedStream.returnOnRead(DATA_VALUE);
eofstream.read();
Assert.assertFalse(eofstream.isSelfClosed());
}
@Test
public void whenNotAtEOF_readReturnsNextByteFromWrappedStream() throws Exception {
wrappedStream.returnOnRead(DATA_VALUE);
assertThat(eofstream.read(), is(DATA_VALUE));
}
34. @Test
public void afterSuccessfulRead_haveOriginalWrappedStream() throws Exception {
wrappedStream.returnOnRead(DATA_VALUE);
eofstream.read();
assertThat(eofstream.getWrappedStream(), sameInstance((InputStream) wrappedStream));
}
@Test
public void whenNoData_readReturnsEOF() throws Exception {
assertThat(eofstream.read(), is(EOF));
}
@Test
public void afterEofRead_streamIsNotSelfClosed() throws Exception {
eofstream.read();
assertFalse(eofstream.isSelfClosed());
}
35. @Test
public void afterEofRead_wrappedStreamIsNull() throws Exception {
eofstream.read();
assertThat(eofstream.getWrappedStream(), nullValue());
}
@Test
public void afterEofRead_wrappedStreamIsClosed() throws Exception {
eofstream.read();
assertTrue(wrappedStream.isClosed());
}
@Test
public void afterEofRead_nextReadReturnsEOF() throws Exception {
eofstream.read();
assertThat(eofstream.read(), is(EOF));
}
37. Unit Tests are Executable Documentation
Keep the tests clean
Test one thing per test
Use descriptive names