junit - proideadata.proidea.org.pl/atmosphere/2014/presentations/junit_beyond_the... · who am i?...
TRANSCRIPT
JUNITBeyond the basics
/ Adam Dudczak @maneo
Atmosphere 2014, Warszawa, 19-20.05.2014
WHO AM I?Software Engineer in Allegro groupWorking with Java (and JUnit) since 2004One of the leaders of Co-organizer of conference
Poznań JUGGeeCON
ERRORRATE CLASSpublic 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); }}
SIMPLE ERRORRATE TESTpublic class ErrorRateTest {@Testpublic void shouldCalculateErrorRate() { //given ErrorRate errorRate = new ErrorRate(100, 10);
//when String result = errorRate.getErrorRateAsString();
//then assertThat(result, is("0.01")); }}
SIMPLE ERRORRATE TESTObject[][] testParameters = new Object[][]{ new Object[]{100,1,"0.01"}, new Object[]{0,0,"0.00"}, };
@Testpublic 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])); }}
LET'S BRAKE SOMETHING...Object[][] testParameters = new Object[][]{ new Object[]{ 100,1,"0.02"}, new Object []{0,0,"0.02"}, };
First error stops test
All cases are seen as one 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)...@Testpublic void shouldCalculateErrorRate() { //given ErrorRate errorRate = new ErrorRate(totalNumberOfItems, totalNumberOfRejected); //when String result = errorRate.getErrorRateAsString();
//then assertThat(result, is(finalErrorRate));}...
LET'S BRAKE SOMETHING...
Two independent tests
A lot of boilerplate code!
Only one occurance of @Parameters per class
JUNIT-PARAMSParameterised tests that don't suck
Created by Paweł LipińskiA lot of very interesting ideas.
https://code.google.com/p/junitparams/
<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@Parameterspublic 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-PARAMSMore examples can be found at iSamples_of_Usage_Test.java
PersonTest.java
Similar to approach in TestNG
Test code is clear and concise - YEAH!!
ORG.JUNIT.EXPERIMENTAL.THEORIESTheory is, in fact a parameterized testThis approach is more focused on requirements than onparticular test casesSimilar to ScalaCheck (Scala) / QuickCheck (Erlang)
ORG.JUNIT.EXPERIMENTAL.THEORIESTheory describes features of class/method and verifies themusing given set of input dataTheory is executed as one testIf assertion fails for any set of input data whole theory failsExample in ErrorRate_05_Theory_Test.java
RANDOMIZED UNIT TESTING"Monkey testing" - more randomness in your tests
http://labs.carrotsearch.com/randomizedtesting.html
RANDOMIZED UNIT TESTING@Testpublic void randomizedTesting() { // Here we pick two positive integers. // Note superclass utility methods. int a = randomIntBetween(0, Integer.MAX_VALUE); int b = randomIntBetween(0, Integer.MAX_VALUE); int result = Adder.add(a, b); assertTrue(result + " < (" + a + " or " + b + ")?", result >= a && result >= b);}
@THREADLEAKING*Randomized Unit Testing library helps to check/controlthreads activity@ThreadLeaking* annotations verfies if threads are leakingfrom your tests/suiteCheck out ErrorRate_06_Randomized_Test.java
@RULEResuable @Before/@After... and moreExample in JunitRulesShowcaseTestJUnit has several built-in @Rules, ex.:
ExternalResource, ExpectedException, TestName...
@RULEpublic 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 cannot be applied to static fields - use @ClassRule
@RULE IN BETAMAXimport co.freeside.betamax.Betamax;import co.freeside.betamax.Recorder;import org.junit.*;
public class MyTest {
@Rule public Recorder recorder = new Recorder();
@Betamax(tape="my tape") @Test public void testMethodThatAccessesExternalWebService() {
}}
@RULE WITH SPRING AND WIREMOCK@Rule public WireMockRule wireMockRule = new WireMockRule(8089); //Check out http://wiremock.org/
@Rule public TestRule contextRule = new SpringContextRule( new String[] { "testContext.xml" }, this);
@Autowired public String bar;
@Test public void testBar() throws Exception { .... }
JUNIT BENCHMARKS AND TIMEOUTpublic class MyTest { @Rule public TestRule benchmarkRun = new BenchmarkRule(); @Rule public TestTimeout timeoutRule = new TestTimeout(30); @Test public void twentyMillis() throws Exception { Thread.sleep(20); }}
MyTest.twentyMillis: [measured 10 out of 15 rounds]round: 0.02 [+- 0.00], round.gc: 0.00 [+- 0.00], GC.calls: 0, GC.time: 0.00, time.total: 0.32, time.warmup: 0.12, time.bench: 0.20
JUNIT BENCHMARKSChart and persistent results history
http://labs.carrotsearch.com/junit-benchmarks.html
BETTER EXCEPTION HANDLINGExample by Rafał Borowiec ( )blog.codeleak.pl/
public class ExpectedExceptionsTest {
@Rule public ExpectedException thrown = ExpectedException.none();
@Test public void verifiesTypeAndMessage() {
thrown.expect(RuntimeException.class); thrown.expectMessage("Runtime exception occurred");
throw new RuntimeException("Runtime exception occurred"); }}
"NEW" ASSERTIONSassertThat and built-in Hamcrest matchersReadable assertions and better error handling
assertThat("this string", is("this string")); assertThat(theBiscuit, is(equalTo(myBiscuit))); assertThat("this string", containsString("is"));
ALTERNATIVE APPROACH - FEST/ASSERTJAlternative (but in fact a mainstream) way of buildingassertions
gives access to hundreds of assertionsAssertJassertThat(frodo.getName()).isEqualTo("Frodo");assertThat(frodo).isNotEqualTo(sauron) .isIn(fellowshipOfTheRing);assertThat(sauron).isNotIn(fellowshipOfTheRing);
ORGANIZE TESTS IN @SUITE@RunWith(Suite.class)@SuiteClasses({ ErrorRate_01_SimpleTest.class, ErrorRate_03_Parametrized_Test.class})public class SuiteInitializationExample {
@ClassRule public static ExternalResource resource= new ExternalResource() { @Override protected void before() throws Throwable { System.out.println("Starting the heavyweightServer"); };
@Override protected void after() { System.out.println("Stopping the heavyweightServer"); }; };}
CATEGORIES/SUITES AND BUILD TOOLSSuite is a bit Ant-ish - use CategoriesCategories are supported by both and Maven Gradle
<build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <groups>com.ex.FastTests,com.ex.RegressionTests</groups> </configuration> </plugin> </plugins></build>
USE CATEGORIES ON THE @SUITE LEVELpublic class HeavyIntegrationTest { @Test @Category(HeavyWeight.class) public void shouldCalculateErrorRate() { assumeTrue(isHeavyWeightServerRunning()); //heave stuff with heavyWeight server here } ...
USE CATEGORIES ON THE @SUITE LEVEL@RunWith(Categories.class)@IncludeCategory(HeavyWeight.class)@SuiteClasses( { ErrorRate_01_SimpleTest.class, HeavyIntegrationTest.class})public class SuiteWithCategories {
//category marker interface public interface HeavyWeight {}
...
CODE SAMPLESAll examples can be found at:
https://bitbucket.org/maneo/junit-presentation/