JUnit

Parametry, teorie, rules i inni

Adam Dudczak / @maneo


Spotkanie Poznań JUG, 14.01.2014



Agenda

  1. @Parametrized
  2. JUnit-params
  3. @Theory i assumeTrue
  4. Rule - AOP dla JUnit
  5. inne ciekawostki

Repozytorium

Wszystkie przykłady można znaleźć: https://bitbucket.org/maneo/junit-presentation/

Klasa ErrorRate


public class ErrorRate {

 double errorRate;

 public ErrorRate(long noOfItems, long noOfErrors) {
   errorRate = calculateErrorRate(noOfItems, noOfErrors);
 }
 double calculateErrorRate(long noOfItems, long noOfErrors) { 
  ... 
 }
  
 public String getErrorRateAsString() {
   return String.format("%2.02f", errorRate);
 } 
}
                 

Testowanie klasy ErrorRate


public class ErrorRateTest {
@Test
public void shouldCalculateErrorRate() {
  //given
  ErrorRate errorRate = new ErrorRate(100, 10);

  //when
  String result = errorRate.getErrorRateAsString();

  //then
  assertThat(result, is("0.01"));
 }
}                 

Testowanie klasy ErrorRate


Object[][] testParameters = new Object[][]{
    new Object[]{100,1,"0.01"},
    new Object[]{0,0,"0.00"},
  };
    
@Test
public void shouldCalculateErrorRate1() {
 for (int i = 0; i>testParameters.length; i++) {
    ErrorRate errorRate 
     = new ErrorRate((Integer)testParameters[i][0],
                     (Integer)testParameters[i][1]);
    String result = errorRate.getErrorRateAsString();
    assertThat(result, is((String)testParameters[i][2]));
 }
}
  

Psujemy dane...


	Object[][] testParameters = new Object[][]{
		new Object[]{100,1,"0.02"},
		new Object[]{0,0,"0.02"},
	};
	

Pierwszy błąd wstrzymuje testy

Widzimy to jako jeden test

JUnit Parametrized


@RunWith(Parameterized.class)
public class ErrorRateTest {

  @Parameters
  public static Collection>Object[]< data() {
     return Arrays.asList(new Object[][]{
         new Object[]{100,1,"0.01"},
         new Object[]{0,0,"0.00"},});
  }
  ...
 

JUnit Parametrized (2)


...	  
long totalNumberOfItems;
long totalNumberOfRejected;
double finalErrorRate;

public ErrorRateTest(long totalNumberOfItems, 
                     long totalNumberOfRejected, 
                     double finalErrorRate) {
						 
   this.totalNumberOfItems = totalNumberOfItems;
   this.totalNumberOfRejected = totalNumberOfRejected;
   this.finalErrorRate = finalErrorRate;
}
...
 

JUnit Parametrized (3)


...	  
@Test 
public void shouldCalculateErrorRate() {
 //given
 ErrorRate errorRate = new ErrorRate(totalNumberOfItems, 
                                     totalNumberOfRejected);
 //when
 String result = errorRate.getErrorRateAsString();
 
 //then
 assertThat(result, is(finalErrorRate));
}
...
	

Psujemy dane...

2 testy, niezależne od siebie

Masa kodu żeby to osiągnąć!

Jedno wystąpienie @Parameters na klasę

JUnit-params

  • Parameterised tests that don't suck
  • https://code.google.com/p/junitparams/
  • Projekt Pawła Lipińskiego
  • Kilka bardzo fajnych pomysłów.
  • 
    <dependency>
     <groupId>pl.pragmatists</groupId>
     <artifactId>JUnitParams</artifactId>
     <version>1.0.2</version>
     <scope>test</scope>
    </dependency>
    		

JUnit-params - showcase


@RunWith(JUnitParamsRunner.class)
public class Samples_of_Usage_Test {

@Test
@Parameters({"AAA,1", "BBB,2"})
public void params_in_annotation(String p1, Integer p2) { }

@Test
@Parameters
public void params_in_default_method(String p1, Integer p2) { }

private Object parametersForParams_in_default_method() 
{ 
	return $($("AAA", 1), $("BBB", 2)); 
}
  

JUnit-params - showcase


@Test
@Parameters(method = "named2,named3")
public void params_in_multiple_methods(String p1, Integer p2) { }
 private Object named2() { return $($("AAA", 1)); }
 private Object named3() { return $($("BBB", 2)); }

@Test
@Parameters(source 
             = ParametersProvidersTest.OneIntegerProvider.class)
public void parameters_from_external_class(int integer) { }
  

JUnit-params - showcase


@Test
@FileParameters("src/test/resources/test.csv")
public void load_params_from_csv(int age, String name) { }

@Test
@FileParameters(value = "src/test/resources/test.csv", 
                mapper = PersonMapper.class)
public void load_params_from_any_file(PersonTest.Person person) { }

@Test
@FileParameters("classpath:test.csv")
public void load_params_from_classpath(int age, String name) { }
  

JUnit-params

Więcej przykładów Samples_of_Usage_Test.java i PersonTest.java

Porównywalne do metod stosowanych w TestNG

Całkiem fajne - bardzo czytelny kod

org.junit.experimental.theories

Przykład w ErrorRate_05_Theory_Test.java

Testy opisują teorię jaką spełnia klasa/metoda, weryfikacja przez podanie wszystkich możliwych kombinacji danych

Dla niektórych danych testy mogą nie mieć sensu (assumeTrue)

Uruchamiane jako jeden test

Jeżeli jedna kombinacja nie przejdzie pada cała teoria

@Rule


public static class HasTempFolder {
  @Rule
  public TemporaryFolder folder = new TemporaryFolder();

  @Test
  public void testUsingTempFolder() throws IOException {
    File createdFile = folder.newFile("myfile.txt");
    File createdFolder = folder.newFolder("subfolder");
    // ...
  }
} 
 

@Rule (2)

  • AOP dla testów...
  • Przykład w JunitRulesShowcaseTest
  • W JUnit dostępne następujące gotowe @Rule m.in.
    • ExternalResource - np. uruchamianie i zamykanie Jetty przed testem
    • ExpectedException - np. ustawia spodziewany wyjątek z wnętrza testu
    • TestName - dostęp do nazwy testu w teście

@Rule (3)

  • W JUnit dostępne następujące gotowe @Rule
    • TestTimeout - daje timeout do wszystkich metod testowych w klasie
    • RuleChain - łączenie kilku Rules
  • Dokumentacja: https://github.com/junit-team/junit/wiki/Rules
  • Rule nie mogą być polami statycznymi w klasie, odpada więc inicjalizacja w @BeforeClass

Inne "nowe" rzeczy

  • assertThat i Hamcrest matchers (dokumentacja)
  • assumeThat (dokumentacja)
  • narzucanie kolejności wykonania testów (@FixMethodOrder)

@FixMethodOrder

  • Testy w JUnit powinny być od siebie niezależne
  • Od jakiegoś czasu można jednak narzucić kolejność ich wykonania dzięki wykorzystaniu @FixMethodOrder
  • Trzy dopuszczalne wartości:
    • MethodSorters.JVM
    • MethodSorters.DEFAULT
    • MethodSorters.NAME_ASCENDING

@FixMethodOrder (2)

The basic problem was that Java 7, when using reflection to find methods, doesn't return them in a consistent order. With Java 6, it was pretty much guaranteed that they'd be returned in the order in which they appear in the source file. This is no longer the case.

Przykład w MethodOrderTest.java

Koniec

Dziękuje za uwagę.



adam (at) dudczak.info / @maneo

Praca w PCSS

http://dl.psnc.pl/praca/

st; } } ] });