Dijkstra, The Humble Programmer 1972
Gute Tests
● Mit jedem Test stellen wir sicher, dass die getestete
Funktionalität korrekt umgesetzt ist
● Hoffnung, dass ähnliche Fälle genauso gut funktionieren
(Äquivalenzklassen)
● Änderungen werden leichter möglich, weil sichergestellt wird,
dass die bestehende Funktionalität nicht kaputt geht
● Tests sind die aktuellste Dokumentation
● Jeder Test erzählt eine Geschichte über was passiert und
was zu erwarten ist.
●
Böse Tests
● Fragile Tests: kleine Änderung in der
Implementierung => viele Tests betroffen
● Redundante Tests => Balast
● Triviale Tests
● Unvollständige Tests => falsche Sicherheit
● Tests ohne Asserts
No Assertions
IResult result = format.execute();
System.out.println(result.size());
Iterator iter = result.iterator();
while (iter.hasNext()) {
IResult r = (IResult) iter.next();
System.out.println(r.getMessage());
}
No Assertions
(verbessert)
IResult result = format.execute();
assertThat(result.size()).isEqualTo(3); 1
Iterator iter = result.iterator();
while (iter.hasNext()) {
IResult r = (IResult) iter.next();
assertThat(r.getMessage()).contains("error"); 2
}
Get- Setter
public void testSetGetParam() throws Exception {
String[] tests = {"a", "aaa", "---",
"23121313", "", null};
for (int i = 0; i < tests.length; i++) {
adapter.setParam(tests[i]);
assertEquals(tests[i], adapter.getParam());
}
}
Happy Path
public class FizzBuzzTest {
@Test
public void testMultipleOfThreeAndFivePrintsFizzBuzz() {
assertEquals("FizzBuzz", FizzBuzz.getResult(15));
}
@Test
public void testMultipleOfThreeOnlyPrintsFizz() {
assertEquals("Fizz", FizzBuzz.getResult(93));
}
Happy Path
(verbessert)
@RunWith(JUnitParamsRunner.class)
public class FizzBuzzJUnitTest {
@Test
@Parameters(value = {"15", "30", "75"})
public void testMultipleOfThreeAndFivePrintsFizzBuzz(
int multipleOf3And5) {
assertEquals("FizzBuzz", FizzBuzz.getResult(multipleOf3And5));
}
@Test
@Parameters(value = {"9", "36", "81"})
public void testMultipleOfThreeOnlyPrintsFizz(...
@Test
@Parameters(value = {"10", "55", "100"})
public void testMultipleOfFiveOnlyPrintsBuzz(...
@Test
@Parameters(value = {"2", "16", "23", "47", "52", ...
public void testInputOfEightPrintsTheNumber(...
}
Not Enough Testing
@Test
public class PagerTest {
private static final int PER_PAGE = 10;
public void shouldGiveOffsetZeroWhenOnZeroPage() {
Pager pager = new Pager(PER_PAGE);
assertThat(pager.getOffset()).isEqualTo(0);
}
public void shouldIncreaseOffsetWhenGoingToPageOne() {
Pager pager = new Pager(PER_PAGE);
pager.goToNextPage();
assertThat(pager.getOffset()).isEqualTo(PER_PAGE);
}
}
Not Enough Testing
(verbessert)
Zero, One and Many
Auskommentieren
//@Test
public void testTeaserWithBildPlusMarker()
{
String url = urlBuilder.setBaseUrl(HTTP_START_PATH +
TEASERREIHE_BILDPLUS_MARKER_PATH.setView("module".build();
driver.get(url);
// Teaserreihe
WebElement teaserReiheElement = getRootElement();
BTOTeaserReihe_modulePO teaserReihePO =
new BTOTeaserReihe_modulePO(teaserReiheElement);
List<Resource_teaserPO> teaserPOs =
teaserReihePO.getTeaserPOs();
assertNotNull("Teaser-Page-Objekte fehlen", teaserPOs);
assertEquals(1, teaserPOs.size());
}
(Aus bildcms JSP-Tests)
Auskommentieren
(verbessert)
@Test @Ignore //TODO momentan nicht testbar auf Testsystem
public void testTeaserWithBildPlusMarker()
{
String url = urlBuilder.setBaseUrl(HTTP_START_PATH +
TEASERREIHE_BILDPLUS_MARKER_PATH.setView("module".build();
driver.get(url);
// Teaserreihe
WebElement teaserReiheElement = getRootElement();
BTOTeaserReihe_modulePO teaserReihePO =
new BTOTeaserReihe_modulePO(teaserReiheElement);
List<Resource_teaserPO> teaserPOs =
teaserReihePO.getTeaserPOs();
assertNotNull("Teaser-Page-Objekte fehlen", teaserPOs);
assertEquals(1, teaserPOs.size());
}
Expecting Exceptions Anywhere
@Test(expected=IndexOutOfBoundsException.class)
public void testMyList() {
MyList<Integer> list = new MyList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(3);
list.add(4);
assertTrue(4 == list.get(4));
assertTrue(2 == list.get(1));
assertTrue(3 == list.get(2));
list.get(6);
}
Expecting Exceptions Anywhere
(verbessert)
● Tests aufteilen (Split)
● Exception-Test Lokalisieren
Assertions should be Merciless
@Test
public void shouldRemoveEmailsByState() {
//given
Email pending = createAndSaveEmail("pending","content pending",
"abc@def.com", Email.PENDING);
Email failed = createAndSaveEmail("failed","content failed",
"abc@def.com", Email.FAILED);
Email sent = createAndSaveEmail("sent","content sent",
"abc@def.com", Email.SENT);
//when
emailDAO.removeByState(Email.FAILED);
//then
assertThat(emailDAO.findAll()).excludes(failed);
}
Assertions should be Merciless
(verbessert)
assertThat(emailDAO.findAll(),
contains(pending, sent))
Is Mockito Working Fine?
@Test
public void testFormUpdate() {
// given
Form f = Mockito.mock(Form.class);
Mockito.when(
f.isUpdateAllowed()).thenReturn(true);
// when
boolean result = f.isUpdateAllowed();
// then
assertTrue(result);
}
@Test
public void will_getChangSecurityQuestRgtAndDetails_if_AdvUserhasRuleId25(){
User user = createUser(userId);
user.setAdvanced(true);
PasswordRuleDto passwordRuleDto = new PasswordRuleDto();
passwordRuleDto.setPasswordRuleId(rulId25);
List<PasswordRuleDto> passwordRules = new ArrayList<PasswordRuleDto>();
passwordRules.add(passwordRuleDto);
given(currentUser.getUser()).thenReturn(user);
given(userDAO.readByPrimaryKey(userId)).thenReturn(user);
given(passwordBean.getPasswordRules()).thenReturn(passwordRules);
UserSecurityQuestionDto dto = userChangeSecurityQuestionBean
.getChangSecurityQuestionRgtAndDetails();
assertNotNull(dto.getEmail());
assertNotNull(dto.getFirstName());
assertNotNull(dto.getLastName());
assertEquals(dto.isChangeSecurityQuestion(), true);
}
Why formatting helps
@Test
public void will_getChangSecurityQuestRgtAndDetails_if_AdvUserhasRuleId25(){
// given
User user = createUser(userId);
user.setAdvanced(true);
PasswordRuleDto passwordRuleDto = new PasswordRuleDto();
passwordRuleDto.setPasswordRuleId(rulId25);
List<PasswordRuleDto> passwordRules = new ArrayList<PasswordRuleDto>();
passwordRules.add(passwordRuleDto);
given(currentUser.getUser()).willReturn(user);
given(userDAO.readByPrimaryKey(userId)).willReturn(user);
given(passwordBean.getPasswordRules()).willReturn(passwordRules);
// when
UserSecurityQuestionDto dto = userChangeSecurityQuestionBean
.getChangSecurityQuestionRgtAndDetails();
// then
assertNotNull(dto.getEmail());
assertNotNull(dto.getFirstName());
assertNotNull(dto.getLastName());
assertEquals(dto.isChangeSecurityQuestion(), true);
}
Why formatting helps
(verbessert)
When a Test Name Lies
Should is Better than Test
public void testInsertNewValues() {
//given
//when
reportRepository.updateReport(ReportColumn.DATE,
ReportColumn.PLACE, reportMap(BigDecimal.TEN));
reportRepository.updateReport(ReportColumn.DATE,
ReportColumn.PLACE, reportMap(new BigDecimal("5")));
//then
assertThat(reportRepository
.getCount(ReportColumn.DATE, ReportColumn.PLACE))
.isEqualTo(1);
}
When a Test Name Lies
Should is Better than Test
(korrigiert)
public void shouldOverrideOldReportWithNewValues() {
//given
//when
reportRepository.updateReport(ReportColumn.DATE,
ReportColumn.PLACE, reportMap(BigDecimal.TEN));
reportRepository.updateReport(ReportColumn.DATE,
ReportColumn.PLACE, reportMap(new BigDecimal("5")));
//then
assertThat(reportRepository
.getCount(ReportColumn.DATE, ReportColumn.PLACE))
.isEqualTo(1);
}

Good Tests Bad Tests

  • 2.
    Dijkstra, The HumbleProgrammer 1972
  • 3.
    Gute Tests ● Mitjedem Test stellen wir sicher, dass die getestete Funktionalität korrekt umgesetzt ist ● Hoffnung, dass ähnliche Fälle genauso gut funktionieren (Äquivalenzklassen) ● Änderungen werden leichter möglich, weil sichergestellt wird, dass die bestehende Funktionalität nicht kaputt geht ● Tests sind die aktuellste Dokumentation ● Jeder Test erzählt eine Geschichte über was passiert und was zu erwarten ist. ●
  • 4.
    Böse Tests ● FragileTests: kleine Änderung in der Implementierung => viele Tests betroffen ● Redundante Tests => Balast ● Triviale Tests ● Unvollständige Tests => falsche Sicherheit ● Tests ohne Asserts
  • 5.
    No Assertions IResult result= format.execute(); System.out.println(result.size()); Iterator iter = result.iterator(); while (iter.hasNext()) { IResult r = (IResult) iter.next(); System.out.println(r.getMessage()); }
  • 6.
    No Assertions (verbessert) IResult result= format.execute(); assertThat(result.size()).isEqualTo(3); 1 Iterator iter = result.iterator(); while (iter.hasNext()) { IResult r = (IResult) iter.next(); assertThat(r.getMessage()).contains("error"); 2 }
  • 7.
    Get- Setter public voidtestSetGetParam() throws Exception { String[] tests = {"a", "aaa", "---", "23121313", "", null}; for (int i = 0; i < tests.length; i++) { adapter.setParam(tests[i]); assertEquals(tests[i], adapter.getParam()); } }
  • 8.
    Happy Path public classFizzBuzzTest { @Test public void testMultipleOfThreeAndFivePrintsFizzBuzz() { assertEquals("FizzBuzz", FizzBuzz.getResult(15)); } @Test public void testMultipleOfThreeOnlyPrintsFizz() { assertEquals("Fizz", FizzBuzz.getResult(93)); }
  • 9.
    Happy Path (verbessert) @RunWith(JUnitParamsRunner.class) public classFizzBuzzJUnitTest { @Test @Parameters(value = {"15", "30", "75"}) public void testMultipleOfThreeAndFivePrintsFizzBuzz( int multipleOf3And5) { assertEquals("FizzBuzz", FizzBuzz.getResult(multipleOf3And5)); } @Test @Parameters(value = {"9", "36", "81"}) public void testMultipleOfThreeOnlyPrintsFizz(... @Test @Parameters(value = {"10", "55", "100"}) public void testMultipleOfFiveOnlyPrintsBuzz(... @Test @Parameters(value = {"2", "16", "23", "47", "52", ... public void testInputOfEightPrintsTheNumber(... }
  • 10.
    Not Enough Testing @Test publicclass PagerTest { private static final int PER_PAGE = 10; public void shouldGiveOffsetZeroWhenOnZeroPage() { Pager pager = new Pager(PER_PAGE); assertThat(pager.getOffset()).isEqualTo(0); } public void shouldIncreaseOffsetWhenGoingToPageOne() { Pager pager = new Pager(PER_PAGE); pager.goToNextPage(); assertThat(pager.getOffset()).isEqualTo(PER_PAGE); } }
  • 11.
  • 12.
    Auskommentieren //@Test public void testTeaserWithBildPlusMarker() { Stringurl = urlBuilder.setBaseUrl(HTTP_START_PATH + TEASERREIHE_BILDPLUS_MARKER_PATH.setView("module".build(); driver.get(url); // Teaserreihe WebElement teaserReiheElement = getRootElement(); BTOTeaserReihe_modulePO teaserReihePO = new BTOTeaserReihe_modulePO(teaserReiheElement); List<Resource_teaserPO> teaserPOs = teaserReihePO.getTeaserPOs(); assertNotNull("Teaser-Page-Objekte fehlen", teaserPOs); assertEquals(1, teaserPOs.size()); } (Aus bildcms JSP-Tests)
  • 13.
    Auskommentieren (verbessert) @Test @Ignore //TODOmomentan nicht testbar auf Testsystem public void testTeaserWithBildPlusMarker() { String url = urlBuilder.setBaseUrl(HTTP_START_PATH + TEASERREIHE_BILDPLUS_MARKER_PATH.setView("module".build(); driver.get(url); // Teaserreihe WebElement teaserReiheElement = getRootElement(); BTOTeaserReihe_modulePO teaserReihePO = new BTOTeaserReihe_modulePO(teaserReiheElement); List<Resource_teaserPO> teaserPOs = teaserReihePO.getTeaserPOs(); assertNotNull("Teaser-Page-Objekte fehlen", teaserPOs); assertEquals(1, teaserPOs.size()); }
  • 14.
    Expecting Exceptions Anywhere @Test(expected=IndexOutOfBoundsException.class) publicvoid testMyList() { MyList<Integer> list = new MyList<Integer>(); list.add(1); list.add(2); list.add(3); list.add(3); list.add(4); assertTrue(4 == list.get(4)); assertTrue(2 == list.get(1)); assertTrue(3 == list.get(2)); list.get(6); }
  • 15.
    Expecting Exceptions Anywhere (verbessert) ●Tests aufteilen (Split) ● Exception-Test Lokalisieren
  • 16.
    Assertions should beMerciless @Test public void shouldRemoveEmailsByState() { //given Email pending = createAndSaveEmail("pending","content pending", "abc@def.com", Email.PENDING); Email failed = createAndSaveEmail("failed","content failed", "abc@def.com", Email.FAILED); Email sent = createAndSaveEmail("sent","content sent", "abc@def.com", Email.SENT); //when emailDAO.removeByState(Email.FAILED); //then assertThat(emailDAO.findAll()).excludes(failed); }
  • 17.
    Assertions should beMerciless (verbessert) assertThat(emailDAO.findAll(), contains(pending, sent))
  • 18.
    Is Mockito WorkingFine? @Test public void testFormUpdate() { // given Form f = Mockito.mock(Form.class); Mockito.when( f.isUpdateAllowed()).thenReturn(true); // when boolean result = f.isUpdateAllowed(); // then assertTrue(result); }
  • 19.
    @Test public void will_getChangSecurityQuestRgtAndDetails_if_AdvUserhasRuleId25(){ Useruser = createUser(userId); user.setAdvanced(true); PasswordRuleDto passwordRuleDto = new PasswordRuleDto(); passwordRuleDto.setPasswordRuleId(rulId25); List<PasswordRuleDto> passwordRules = new ArrayList<PasswordRuleDto>(); passwordRules.add(passwordRuleDto); given(currentUser.getUser()).thenReturn(user); given(userDAO.readByPrimaryKey(userId)).thenReturn(user); given(passwordBean.getPasswordRules()).thenReturn(passwordRules); UserSecurityQuestionDto dto = userChangeSecurityQuestionBean .getChangSecurityQuestionRgtAndDetails(); assertNotNull(dto.getEmail()); assertNotNull(dto.getFirstName()); assertNotNull(dto.getLastName()); assertEquals(dto.isChangeSecurityQuestion(), true); } Why formatting helps
  • 20.
    @Test public void will_getChangSecurityQuestRgtAndDetails_if_AdvUserhasRuleId25(){ //given User user = createUser(userId); user.setAdvanced(true); PasswordRuleDto passwordRuleDto = new PasswordRuleDto(); passwordRuleDto.setPasswordRuleId(rulId25); List<PasswordRuleDto> passwordRules = new ArrayList<PasswordRuleDto>(); passwordRules.add(passwordRuleDto); given(currentUser.getUser()).willReturn(user); given(userDAO.readByPrimaryKey(userId)).willReturn(user); given(passwordBean.getPasswordRules()).willReturn(passwordRules); // when UserSecurityQuestionDto dto = userChangeSecurityQuestionBean .getChangSecurityQuestionRgtAndDetails(); // then assertNotNull(dto.getEmail()); assertNotNull(dto.getFirstName()); assertNotNull(dto.getLastName()); assertEquals(dto.isChangeSecurityQuestion(), true); } Why formatting helps (verbessert)
  • 21.
    When a TestName Lies Should is Better than Test public void testInsertNewValues() { //given //when reportRepository.updateReport(ReportColumn.DATE, ReportColumn.PLACE, reportMap(BigDecimal.TEN)); reportRepository.updateReport(ReportColumn.DATE, ReportColumn.PLACE, reportMap(new BigDecimal("5"))); //then assertThat(reportRepository .getCount(ReportColumn.DATE, ReportColumn.PLACE)) .isEqualTo(1); }
  • 22.
    When a TestName Lies Should is Better than Test (korrigiert) public void shouldOverrideOldReportWithNewValues() { //given //when reportRepository.updateReport(ReportColumn.DATE, ReportColumn.PLACE, reportMap(BigDecimal.TEN)); reportRepository.updateReport(ReportColumn.DATE, ReportColumn.PLACE, reportMap(new BigDecimal("5"))); //then assertThat(reportRepository .getCount(ReportColumn.DATE, ReportColumn.PLACE)) .isEqualTo(1); }