atmosphere 2014: junit: beyond the basics - adam dudczak

41
JUNIT Beyond the basics / Adam Dudczak @maneo Atmosphere 2014, Warszawa, 19-20.05.2014

Upload: proidea

Post on 09-May-2015

418 views

Category:

Presentations & Public Speaking


0 download

DESCRIPTION

Yes... I've heard about Spock ;-) and most likely dozens of other testing frameworks which were created after JUnit started to exist. JUnit is old, it's sometimes a bit grumpy, nevertheless it is stil very concise and lightweight library. It is enough to spend 10 minutes with JUnit documentation to understand the basic elements of test, how to write an assertion and how to launch your first test. In a day to day job there are several other JUnit features which might be very handy, there are also a few curosities worth to mention. In this presentation I will try to cover most of them including: parameterized tests, theories (org.junit.experimental.theories), rules (@Rule) and common matchers. As was already said JUnit is not a newbie in a testing framework world, over the years several additional libraries were added, I will show you a few interesting once, including randomized testing, benchmarking and REST interactions testing. This presentation was developed with the support of Poznań JUG, Tricity JUG and Szczecin JUG who were hosting this lecture in the past. Discussion which we had during and after the meetings was a great aid and influenced heavily current content of this presentation. Adam Dudczak - Adam Dudczak (@maneo), software engineer in Allegro group, working with Java (and JUnit) since 2004. Currently member of Allegro search team working on a better search experience in Allegro.pl. One of the leaders of Poznań JUG (www.jug.poznan.pl) and co-organizer of GeeCON conference (geecon.org). Father and husband, occasionally blogs at dudczak.info/dry.

TRANSCRIPT

Page 1: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

JUNITBeyond the basics

/ Adam Dudczak @maneo

Atmosphere 2014, Warszawa, 19-20.05.2014

Page 2: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

WHO AM I?Software Engineer in Allegro groupWorking with Java (and JUnit) since 2004One of the leaders of Co-organizer of conference

Poznań JUGGeeCON

Page 3: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

WHY JUNIT?

source: wikipedia

Page 4: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

THERE IS NO SPOCK...

source: http://bit.ly/R0r8Ox

Page 5: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

WHY NOT?

source: http://bit.ly/1jNoT8f

Page 6: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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); }}

Page 7: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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")); }}

Page 8: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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])); }}

Page 9: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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

Page 10: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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"},}); } ...

Page 11: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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;}...

Page 12: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

JUNIT PARAMETRIZED (3)...@Testpublic void shouldCalculateErrorRate() { //given ErrorRate errorRate = new ErrorRate(totalNumberOfItems, totalNumberOfRejected); //when String result = errorRate.getErrorRateAsString();

//then assertThat(result, is(finalErrorRate));}...

Page 13: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

LET'S BRAKE SOMETHING...

Two independent tests

A lot of boilerplate code!

Only one occurance of @Parameters per class

Page 14: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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>

Page 15: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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));}

Page 16: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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) { }

Page 17: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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) { }

Page 18: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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!!

Page 19: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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)

Page 20: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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

Page 21: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

RANDOMIZED UNIT TESTING"Monkey testing" - more randomness in your tests

http://labs.carrotsearch.com/randomizedtesting.html

Page 22: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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);}

Page 23: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

SOMETHING WENT WRONG!

@Seed("2300CE9BBBCFF4C8:573D00C2ABB4AD89")

Page 24: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

@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

Page 25: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

@RULE

source: http://bit.ly/1jNpE13

Page 26: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

@RULEResuable @Before/@After... and moreExample in JunitRulesShowcaseTestJUnit has several built-in @Rules, ex.:

ExternalResource, ExpectedException, TestName...

Page 27: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

@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

Page 28: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

@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() {

}}

Page 29: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

@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 { .... }

Page 30: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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

Page 31: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

JUNIT BENCHMARKSChart and persistent results history

http://labs.carrotsearch.com/junit-benchmarks.html

Page 32: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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"); }}

Page 33: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

"NEW" ASSERTIONS

source: http://bit.ly/1taOW0H

Page 34: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

"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"));

Page 35: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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);

Page 36: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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"); }; };}

Page 37: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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>

Page 38: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

USE CATEGORIES ON THE @SUITE LEVELpublic class HeavyIntegrationTest { @Test @Category(HeavyWeight.class) public void shouldCalculateErrorRate() { assumeTrue(isHeavyWeightServerRunning()); //heave stuff with heavyWeight server here } ...

Page 39: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

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 {}

...

Page 40: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

CODE SAMPLESAll examples can be found at:

https://bitbucket.org/maneo/junit-presentation/

Page 41: Atmosphere 2014: JUnit: beyond the basics - Adam Dudczak

THAT'S ALL FOLKSThank you for your attention.

adam (at) dudczak.info / @maneo