Bessere Tests mit JUnit 4.x
Marc Philipp
24. Juni 2010
Über JUnit


                Kent Beck:
                A programmer-oriented testing
                framework for Java.
...
Warum ein Vortrag über JUnit?



        Jeder kennt JUnit.
        Wirklich?
        Jeder meint, JUnit zu kennen.
      ...
Neue Features seit Version 4.0


        Matchers                        Rules



                        JUnit 4.8.2



 ...
Matchers




René Magritte, Untitled              5
Neue Assertion: assertThat(...)


        Neue Assert-Methoden:
        <T> void assertThat(T actual, Matcher<T> matcher)
...
Bessere Lesbarkeit

 import static org.hamcrest.CoreMatchers.is;

 public class BetterReadability {

     @Test public voi...
Matcher kombinieren




 Matcher lassen sich einfach kombinieren:
   assertThat(1 + 1, is(not(3)));
   assertThat(1 + 1, b...
Aussagekräftige Fehlermeldungen
 Herkömmliche Zusicherung ohne Beschreibung
   assertFalse(asList(1, 2, 3).contains(2));

...
Vordefinierte Matcher


        Vielzahl vordefinierter Matcher:
              Core is, not, allOf, anyOf, nullValue, . . .
...
Ein eigener Matcher
Implementierung


  public class IsEmptyCollection extends TypeSafeMatcher<Collection<?>> {

      @Ov...
Ein eigener Matcher
Benutzung




  public class CollectionMatchersTest {

      @Test public void isEmpty() {
         Tr...
Viele Matcher – Viele Klassen


 Problem
        Vielzahl von Klassen mit zu importierenden statischen Methoden
        Ma...
Generierung einer Matcher-Bibliothek
   1. Konfiguration in XML
        <matchers>
           <factory class="org.hamcrest....
Fazit: Matchers

        Assertions lassen sich oft eleganter formulieren
        Manchmal sind die alten Assertion-Method...
Theories




Wassily Kandinsky, Composition VIII              16
Parametrisierte Tests


        Seit JUnit 4.0 gibt es den Parameterized Test Runner.
        Ermöglicht Parametrisierung ...
Parametrisierte Tests
Beispiel: Fibonacci-Zahlen
  @RunWith(Parameterized.class)
  public class FibonacciParameterizedTest...
Theories



        Neu seit Version 4.4
        Parametrisierte Testmethode mit Vorbedingungen (Assumptions)
            ...
Theories
Beispiel: Fibonacci-Zahlen

  import static de.andrena.junit.fibonacci.Fibonacci.compute;

  @RunWith(Theories.cl...
Theories
Beispiel: Verschiedene @DataPoints

  @RunWith(Theories.class)
  public class DifferentDataPoints {

      @DataP...
Parameterized Tests vs. Theories


        Definition von @DataPoints ist flexibler
                Konstanten oder Methoden...
Herkömmliche Tests vs. Theories


        Herkömmliche Tests benutzen Beispiele:
                Überprüfung des Verhalten...
Rules




René Magritte, Golconda           24
Was ist eine Rule?


 Erweiterungsmechanismus für Ablauf der Testmethoden
 Beispiele:
        Ausführung eigenen Codes vor...
Beispiel: TemporaryFolder
Ohne Benutzung einer Rule

  public class TemporaryFolderWithoutRule {
     private File folder;...
Beispiel: TemporaryFolder
Unter Verwendung einer Rule




  public class TemporaryFolderWithRule {

      @Rule public Tem...
Was ist eine Rule?


 Erweiterungsmechanismus für Ablauf der Testmethoden
 Beispiele:
        Ausführung eigenen Codes vor...
Beispiel: ExpectedException
Ohne Benutzung einer Rule

  public class ExpectedExceptionWithoutRule {

      int[] threeNum...
Beispiel: ExpectedException
Unter Verwendung einer Rule

  public class ExpectedExceptionWithRule {

      int[] threeNumb...
Weitere vordefinierte Rules


 ErrorCollector
 Sammelt fehlgeschlagene Assertions innerhalb einer Testmethode und
 gibt am ...
Schreib deine eigenen Regeln!




24. Juni 2010                   32
Eine eigene Regel
Implementierung
  public class SystemProperty extends ExternalResource {
     private final String key, ...
Eine eigene Regel
Benutzung




  public class SomeTestUsingSystemProperty {

      private static final String VALUE = "s...
Vorteile von Regeln


 Wiederverwendbarkeit
 Ermöglichen häufig benötigten Code auszulagern.

 Kombinierbarkeit
 Beliebig v...
Categories




Piet Mondrian, Composition with Gray and Light Brown   36
Tests in Kategorien einteilen
        Kategorie ist Klasse oder Interface:
        public interface Slow {}


        Einz...
Tests in bestimmten Kategorien ausführen
        Herkömmliche Test-Suite:
        @RunWith(Suite.class)
        @SuiteClas...
Ausblick




Quint Buchholz, Mann auf einer Leiter              39
Was jetzt?



        Aktualisierung auf neue Version ist einfach
        Alte Tests funktionieren weiterhin
        Neue ...
Ausprobieren!
 http://www.junit.org/


    Grau is alle Theorie – entscheidend is auf’m Platz
                            ...
Upcoming SlideShare
Loading in …5
×

Bessere Tests mit JUnit 4.x

6,471 views
6,393 views

Published on

gal, ob man testgetriebene Entwicklung einsetzt oder konventionell entwickelt, das Schreiben von Unit Tests gehört mittlerweile zum etablierten Standard in der Software-Entwicklung. JUnit ist das älteste und bekannteste Unit Testing Framework für Java.

Seit der Umstellung auf Annotationen in JUnit 4.0 hat sich einiges getan. Zu den Neuerungen zählen unter anderem ein komplett neuer Assertion-Mechanismus, die Möglichkeit, Vorbedingungen für Testfälle anzugeben und ein neuer Regel-basierter Ansatz, der es erlaubt, Code auf wiederverwendbare Art und Weise vor oder nach Testfällen auszuführen.

Die Neuerungen bieten Entwicklern deutlich einfachere und zugleich mächtigere Möglichkeiten, Assertions zu formulieren, Tests zu schreiben und zu strukturieren. Das Resultat ist kompakterer Code, der besser lesbar und somit leichter wartbar ist.

Leider werden diese Innovationen von vielen Entwicklern entweder gar nicht oder erst verzögert wahrgenommen. Diese Session möchte daher auf die Verbesserungen zwischen JUnit 4.0 und der aktuellen Version 4.8.1 aufmerksam machen und dazu motivieren, auf neuere Versionen umzusteigen.

Published in: Technology, Education
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
6,471
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Bessere Tests mit JUnit 4.x

  1. 1. Bessere Tests mit JUnit 4.x Marc Philipp 24. Juni 2010
  2. 2. Über JUnit Kent Beck: A programmer-oriented testing framework for Java. David Saff: JUnit is the intersection of all possible useful Java test frameworks, not their union. Nicht nur für Unit Tests! 24. Juni 2010 2
  3. 3. Warum ein Vortrag über JUnit? Jeder kennt JUnit. Wirklich? Jeder meint, JUnit zu kennen. Ich meinte es auch. Seit Version 4.0 hat sich einiges getan! 24. Juni 2010 3
  4. 4. Neue Features seit Version 4.0 Matchers Rules JUnit 4.8.2 Theories Categories 24. Juni 2010 4
  5. 5. Matchers René Magritte, Untitled 5
  6. 6. Neue Assertion: assertThat(...) Neue Assert-Methoden: <T> void assertThat(T actual, Matcher<T> matcher) <T> void assertThat(String reason, T actual, Matcher<T> matcher) Parameter: reason Zusätzliche Beschreibung für den Fehlerfall (optional) actual Tatsächlicher Wert matcher Hamcrest-Matcher überprüft tatsächlichen Wert 24. Juni 2010 6
  7. 7. Bessere Lesbarkeit import static org.hamcrest.CoreMatchers.is; public class BetterReadability { @Test public void withoutMatchers() { assertEquals(2, 1 + 1); } @Test public void withMatchers() { assertThat(1 + 1, is(2)); } } Reihenfolge stimmt Häufig besser lesbar als herkömmliche Assertions 24. Juni 2010 7
  8. 8. Matcher kombinieren Matcher lassen sich einfach kombinieren: assertThat(1 + 1, is(not(3))); assertThat(1 + 1, both(greaterThan(1)).and(lessThan(3))); 24. Juni 2010 8
  9. 9. Aussagekräftige Fehlermeldungen Herkömmliche Zusicherung ohne Beschreibung assertFalse(asList(1, 2, 3).contains(2)); Ergebnis: AssertionError: at de.andrena.junit... Neue Zusicherung mit Matcher assertThat(asList(1, 2, 3), not(hasItem(2))); Ergebnis: AssertionError: Expected: not a collection containing <2> got: <[1, 2, 3]> 24. Juni 2010 9
  10. 10. Vordefinierte Matcher Vielzahl vordefinierter Matcher: Core is, not, allOf, anyOf, nullValue, . . . Strings containsString, startsWith, endsWith, . . . Collections hasItem, hasItems, isIn, empty, hasSize, . . . Bei JUnit ist nur ein kleiner Teil mit dabei: org.hamcrest.CoreMatchers org.junit.matchers.JUnitMatchers Hamcrest bietet weitere Matcher (hamcrest-all.jar). Darüber hinaus lassen sich eigene Matcher definieren. 24. Juni 2010 10
  11. 11. Ein eigener Matcher Implementierung public class IsEmptyCollection extends TypeSafeMatcher<Collection<?>> { @Override protected boolean matchesSafely(Collection<?> collection) { return collection.isEmpty(); } @Override public void describeTo(Description description) { description.appendText("empty"); } @Factory public static Matcher<Collection<?>> empty() { return new IsEmptyCollection(); } } 24. Juni 2010 11
  12. 12. Ein eigener Matcher Benutzung public class CollectionMatchersTest { @Test public void isEmpty() { TreeSet<String> set = new TreeSet<String>(); assertThat(set, new IsEmptyCollection()); // direkter Aufruf assertThat(set, IsEmptyCollection.empty()); // über @Factory-Methode assertThat(set, empty()); // mit statischem Import assertThat(set, is(empty())); // syntactic sugar } } 24. Juni 2010 12
  13. 13. Viele Matcher – Viele Klassen Problem Vielzahl von Klassen mit zu importierenden statischen Methoden Manuelle Konfiguration der IDE notwendig . . . bei allen Entwicklern Lösung Generierung einer eigenen Matcher-Bibliothek 24. Juni 2010 13
  14. 14. Generierung einer Matcher-Bibliothek 1. Konfiguration in XML <matchers> <factory class="org.hamcrest.core.Is"/> <factory class="de.andrena.junit.hamcrest.IsEmptyCollection"/> </matchers> 2. Aufruf von org.hamcrest.generator.config.XmlConfigurator 3. Alle mit @Factory annotierten Methoden werden in einer generierten Klasse zusammengefasst: public class Matchers { public static <T> Matcher<? super T> is(T param1) { return org.hamcrest.core.Is.is(param1); } /* ... */ public static Matcher<Collection<?>> empty() { return de.andrena.junit.hamcrest.IsEmptyCollection.empty(); } } 24. Juni 2010 14
  15. 15. Fazit: Matchers Assertions lassen sich oft eleganter formulieren Manchmal sind die alten Assertion-Methoden klarer Problem Javas Typsystem macht einem oft einen Strich durch die Rechnung Boxing notwendig bei primitiven Typen assertThat(1 + 1, is(2)) Mangelnde Typinferenz assertThat(new TreeSet<String>(), Matchers.<String>empty()); 24. Juni 2010 15
  16. 16. Theories Wassily Kandinsky, Composition VIII 16
  17. 17. Parametrisierte Tests Seit JUnit 4.0 gibt es den Parameterized Test Runner. Ermöglicht Parametrisierung von Testklassen über Konstruktor Argumente werden in einer statischen Methode angegeben: @Parameters-Annotation Rückgabetyp: List<Object[]> Für jedes Paar von Testmethode und Argument wird eine Instanz der Testklasse angelegt und die Testmethode ausgeführt. Nützlich, wenn Berechnung mit vielen Eingabeparametern getestet werden soll. 24. Juni 2010 17
  18. 18. Parametrisierte Tests Beispiel: Fibonacci-Zahlen @RunWith(Parameterized.class) public class FibonacciParameterizedTest { @Parameters public static List<Object[]> data() { return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } }); } private final int input, expected; public FibonacciParameterizedTest(int input, int expected) { this.input = input; this.expected = expected; } @Test public void test() { assertEquals(expected, Fibonacci.compute(input)); } } 24. Juni 2010 18
  19. 19. Theories Neu seit Version 4.4 Parametrisierte Testmethode mit Vorbedingungen (Assumptions) assumeThat() assumeTrue() assumeNotNull() @Theory statt @Test Input für Testmethoden über @DataPoint(s)-Annotation 24. Juni 2010 19
  20. 20. Theories Beispiel: Fibonacci-Zahlen import static de.andrena.junit.fibonacci.Fibonacci.compute; @RunWith(Theories.class) public class FibonacciTheories { @DataPoints public static int[] VALUES = { 0, 1, 2, 3, 4, 5, 6 }; @Theory public void seeds(int n) { // F (0) = 0, F (1) = 1 assumeTrue(n <= 1); assertEquals(n, compute(n)); } @Theory public void recurrence(int n) { // F (n) = F (n − 1) + F (n − 2) für n > 1 assumeTrue(n > 1); assertEquals(compute(n - 1) + compute(n - 2), compute(n)); } } 24. Juni 2010 20
  21. 21. Theories Beispiel: Verschiedene @DataPoints @RunWith(Theories.class) public class DifferentDataPoints { @DataPoint public static String A = "a"; @DataPoint public static String B = "b"; @DataPoints public static int[] NUMBERS = { 1, 2, 3 }; @Theory public void theory1(String string) { /* ("a"), ("b") */ } @Theory public void theory2(int number) { /* (1), (2), (3) */ } @Theory public void theory3(String string, int number) { /* ("a", 1), ("a", 2), ("a", 3), ("b", 1), ("b", 2), ("b", 3) */ } } 24. Juni 2010 21
  22. 22. Parameterized Tests vs. Theories Definition von @DataPoints ist flexibler Konstanten oder Methoden Beliebig viele Verschiedene Typen Parametrisierung der Testmethoden Kein Boilerplate-Code (Konstruktor + Instanzvariablen) Verschiedene Parametertypen möglich Theories können alles, was Parameterized auch kann, aber mehr! 24. Juni 2010 22
  23. 23. Herkömmliche Tests vs. Theories Herkömmliche Tests benutzen Beispiele: Überprüfung des Verhaltens unter ausgewählten Eingaben Beispiele sind (hoffentlich) charakteristisch Eine Theory verallgemeinert eine Menge von Tests: Vorbedingung wird explizit angegeben Sollte für alle Eingaben gelten, die Vorbedingungen erfüllen Eingabewerte können explizit angegeben werden oder automatisch erzeugt werden 24. Juni 2010 23
  24. 24. Rules René Magritte, Golconda 24
  25. 25. Was ist eine Rule? Erweiterungsmechanismus für Ablauf der Testmethoden Beispiele: Ausführung eigenen Codes vor bzw. nach jeder Testmethode Behandlung fehlgeschlagener Tests Überprüfung zusätzlicher Kriterien nach einem Tests ... 24. Juni 2010 25
  26. 26. Beispiel: TemporaryFolder Ohne Benutzung einer Rule public class TemporaryFolderWithoutRule { private File folder; @Before public void createTemporaryFolder() throws Exception { folder = File.createTempFile("myFolder", ""); folder.delete(); folder.mkdir(); } @Test public void test() throws Exception { File file = new File(folder, "test.txt"); file.createNewFile(); assertTrue(file.exists()); } @After public void deleteTemporaryFolder() { recursivelyDelete(folder); // passt nicht mehr auf die Folie... } 24. Juni 2010 26
  27. 27. Beispiel: TemporaryFolder Unter Verwendung einer Rule public class TemporaryFolderWithRule { @Rule public TemporaryFolder folder = new TemporaryFolder(); @Test public void test() throws Exception { File file = folder.newFile("test.txt"); assertTrue(file.exists()); } } 24. Juni 2010 27
  28. 28. Was ist eine Rule? Erweiterungsmechanismus für Ablauf der Testmethoden Beispiele: Ausführung eigenen Codes vor bzw. nach jeder Testmethode Behandlung fehlgeschlagener Tests Überprüfung zusätzlicher Kriterien nach einem Tests ... 24. Juni 2010 28
  29. 29. Beispiel: ExpectedException Ohne Benutzung einer Rule public class ExpectedExceptionWithoutRule { int[] threeNumbers = { 1, 2, 3 }; @Test(expected = ArrayIndexOutOfBoundsException.class) public void exception() { threeNumbers[3] = 4; } @Test public void exceptionWithMessage() { try { threeNumbers[3] = 4; fail("ArrayIndexOutOfBoundsException expected"); } catch (ArrayIndexOutOfBoundsException expected) { assertEquals("3", expected.getMessage()); } } } 24. Juni 2010 29
  30. 30. Beispiel: ExpectedException Unter Verwendung einer Rule public class ExpectedExceptionWithRule { int[] threeNumbers = { 1, 2, 3 }; @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void exception() { thrown.expect(ArrayIndexOutOfBoundsException.class); threeNumbers[3] = 4; } @Test public void exceptionWithMessage() { thrown.expect(ArrayIndexOutOfBoundsException.class); thrown.expectMessage("3"); threeNumbers[3] = 4; } } 24. Juni 2010 30
  31. 31. Weitere vordefinierte Rules ErrorCollector Sammelt fehlgeschlagene Assertions innerhalb einer Testmethode und gibt am Ende eine Liste der Fehlschläge aus. TestName Merkt sich Namen der aktuell ausgeführten Testmethode und stellt ihn auf Anfrage zur Verfügung. Timeout Wendet gleichen Timeout auf alle Testmethoden einer Klasse an. 24. Juni 2010 31
  32. 32. Schreib deine eigenen Regeln! 24. Juni 2010 32
  33. 33. Eine eigene Regel Implementierung public class SystemProperty extends ExternalResource { private final String key, value; private String oldValue; public SystemProperty(String key, String value) { this.key = key; this.value = value; } @Override protected void before() { oldValue = System.getProperty(key); System.setProperty(key, value); } @Override protected void after() { if (oldValue == null) { System.getProperties().remove(key); } else { System.setProperty(key, oldValue); } } } 24. Juni 2010 33
  34. 34. Eine eigene Regel Benutzung public class SomeTestUsingSystemProperty { private static final String VALUE = "someValue"; private static final String KEY = "someKey"; @Rule public SystemProperty systemProperty = new SystemProperty(KEY, VALUE); @Test public void test() { assertThat(System.getProperty(KEY), is(VALUE)); } } 24. Juni 2010 34
  35. 35. Vorteile von Regeln Wiederverwendbarkeit Ermöglichen häufig benötigten Code auszulagern. Kombinierbarkeit Beliebig viele Regeln in einem Test verwendbar Delegation statt Vererbung Helfen Testklassenhierarchien zu vermeiden! Erweiterbarkeit Eigene Regeln schreiben ist einfach. 24. Juni 2010 35
  36. 36. Categories Piet Mondrian, Composition with Gray and Light Brown 36
  37. 37. Tests in Kategorien einteilen Kategorie ist Klasse oder Interface: public interface Slow {} Einzelner Test: @Category(Slow.class) @Test public void test() {} Alle Tests einer Klasse: @Category(Slow.class) public class B { @Test public void c() {} } 24. Juni 2010 37
  38. 38. Tests in bestimmten Kategorien ausführen Herkömmliche Test-Suite: @RunWith(Suite.class) @SuiteClasses( { A.class, B.class }) public class AllTests {} Alle Tests in der Kategorie Slow: @RunWith(Categories.class) @IncludeCategory(Slow.class) public class AllSlowTests extends AllTests {} Alle anderen Tests: @RunWith(Categories.class) @ExcludeCategory(Slow.class) public class AllFastTests extends AllTests {} 24. Juni 2010 38
  39. 39. Ausblick Quint Buchholz, Mann auf einer Leiter 39
  40. 40. Was jetzt? Aktualisierung auf neue Version ist einfach Alte Tests funktionieren weiterhin Neue Tests profitieren von neuen Features Alte Tests können nach und nach vereinfacht werden Ausprobieren! 24. Juni 2010 40
  41. 41. Ausprobieren! http://www.junit.org/ Grau is alle Theorie – entscheidend is auf’m Platz – Adi Preissler Vielen Dank! Mail marc@andrena.de Twitter marcphilipp Blog http://marcphilipp.tumblr.com/ 24. Juni 2010 41

×