Top Banner
Developer Tests Things to Know Vaidas Pilkauskas 2014 Kaunas JUG [email protected]
108

Developer Test - Things to Know

Dec 05, 2014

Download

Engineering

vilniusjug

Presenation slides given at Kaunas JUG 2014-10-15
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Developer Test - Things to Know

Developer TestsThings to Know

Vaidas Pilkauskas 2014 Kaunas JUG

[email protected]

Page 2: Developer Test - Things to Know

“We couldn’t understand why people without technical knowledge had to tell programmers “what” to do and, furthermore, they had to supervise “how” programmers did it.”

Cristian Rennellahttp://qz.com/260846/why-our-startup-has-no-bosses-no-office-and-a-four-day-work-week/

Page 3: Developer Test - Things to Know

What I’m going to talk about

● Things we argue about during code reviews● Things that took me time to understand and

prove that they are actually good way to go● Small things we have no time to discuss

during big talks

Page 4: Developer Test - Things to Know
Page 5: Developer Test - Things to Know

“Legacy Code is code without Tests”

Michael FeathersWorking Effectively with Legacy Code

Page 6: Developer Test - Things to Know

So what is test?

It is system’s exercise under predefined conditions and then verification of an expected outcome.

Page 7: Developer Test - Things to Know

Thing #1Test phases

Page 8: Developer Test - Things to Know

Test phases

1. Set up2. Exercise 3. Verify4. Tear down

Page 9: Developer Test - Things to Know

Test phases@Test public void serverShouldExecuteJobSuccessfully() {

Server server = new Server(); // set up

Job job = new Job(); // set up

Status status = server.execute(job); // exercise

assertEquals(SUCCESS, status); // verify

server.shutdown(); // tear down

}

Page 10: Developer Test - Things to Know

@Before public void before() {

server = new Server();

}

@Test public void serverShouldExecuteJobSuccessfully() {

Job job = new Job(); // set up

Status status = server.execute(job); // exercise

assertEquals(SUCCESS, status); // verify

server.shutdown(); // tear down

}

Page 11: Developer Test - Things to Know

@Before public void before() {

server = new Server();

Job job = new Job();

}

@Test public void serverShouldExecuteJobSuccessfully() {

Status status = server.execute(job); // exercise

assertEquals(SUCCESS, status); // verify

server.shutdown(); // tear down

}

Page 12: Developer Test - Things to Know

@Test public void serverShouldQueueJobWithFutureDate() {

// * set up which is actual for the current method

// * use scope specific name

Job futureJob = new Job(futureDate()); // set up

Status status = server.execute(futureJob); // exercise

assertEquals(SUCCESS, status); // verify

server.shutdown(); // tear down

}

Page 13: Developer Test - Things to Know

@Before public void before() {

server = new Server();

Job job = new Job();

}

@Test public void serverShouldExecuteJobSuccessfully() {

Status status = server.execute(job); // exercise

assertEquals(SUCCESS, status); // verify

}

@After public void after() {

server.shutdown(); // tear down

}

Page 14: Developer Test - Things to Know

@Before ..

@Test public void serverShouldExecuteJobSuccessfully() {

// * no need to name intermediate var, but

// * may hide return meaning of server.execute()

// execute & verify

assertEquals(SUCCESS, server.execute(job));

}

@After ..

Page 15: Developer Test - Things to Know

Set up

● DRY principle

Page 16: Developer Test - Things to Know

Set up

● DRY principle● Readability

Page 17: Developer Test - Things to Know

Set up

● DRY principle● Readability● Consistency

Page 18: Developer Test - Things to Know

Set up

● DRY principle● Readability● Consistency● Complexity

Page 19: Developer Test - Things to Know

Refactoring

Refactoring is about improving the design of existing code. It is the process of changing a software system in such a way that it does not alter the external behavior of the code, yet improves its internal structure.

Martin FowlerRefactoring: Improving the Design of Existing Code

Page 20: Developer Test - Things to Know

Thing #2What do we test?

Page 21: Developer Test - Things to Know

Test behaviour not methods

● Think of a contract

Page 22: Developer Test - Things to Know

Test behaviour not methods

● Think of a contract ● And responsibilities

Page 23: Developer Test - Things to Know

Test behaviour not methods

● Think of a contract ● And responsibilities● Specify requirements as tests

Page 24: Developer Test - Things to Know

Test behaviour not methods

● Think of a contract ● And responsibilities● Specify requirements as tests● Happens naturally when done in test-first

approach

Page 25: Developer Test - Things to Know

Thing #3Matchers

Page 26: Developer Test - Things to Know

Matchers

● Enhanced readability● Assertions on the right level of abstraction● Encapsulate testing logic● Reusable● Detailed match error messages (do not

leave them out in your custom matchers!)

Page 27: Developer Test - Things to Know

Matcher libraries

● Hamcrest - standard matcher lib for JUnit● AssertJ - fluent assertions (IDE friendly)

● Bring common matchers for you to use● Write your own custom matchers

Page 28: Developer Test - Things to Know

HamcrestassertThat(theBiscuit, equalTo(myBiscuit));

assertThat(theBiscuit, is(equalTo(myBiscuit)));

assertThat(theBiscuit, is(myBiscuit));

Page 29: Developer Test - Things to Know

AssertJassertThat(frodo.getName()).isEqualTo("Frodo");

assertThat(frodo).isNotEqualTo(sauron) .isIn(fellowshipOfTheRing);

assertThat(sauron).isNotIn(fellowshipOfTheRing);

Page 30: Developer Test - Things to Know

Thing #4Custom matchers

Page 31: Developer Test - Things to Know

Custom matchers

Are matchers we develop specifically for our projects

Page 32: Developer Test - Things to Know

Custom matchers

● Help communicate test intention● Abstract assertion logic in case standard

matchers are not enough● Are reusable and save time in large projects● You may have custom message to be more

specific about test failure

Page 33: Developer Test - Things to Know

Custom matchers@Test

public void testBookIsbn() {

Book book = new Book(1l, "5555", "A book");

assertThat(book, hasIsbn("1234"));}

Page 34: Developer Test - Things to Know

Thing #5Failing test

Page 35: Developer Test - Things to Know

fail()

In some cases like testing exceptions you may want to force test to fail if some expected situation does not happen

Page 36: Developer Test - Things to Know

fail()

try{ // do stuff... fail("Exception not thrown");}catch(Exception e){ assertTrue(e.hasSomeFlag());}

Page 37: Developer Test - Things to Know

fail()

● Fundamentally not bad, but better use matchers for expected failure

● Matchers help to clarify test intention● Don’t forget - expected behaviour is an

opposite of a failing test

Page 38: Developer Test - Things to Know

Thing #6Anti-pattern: The Ugly Mirror

Page 39: Developer Test - Things to Know

Anti-pattern: The Ugly Mirror@Test

public void personToStringShouldIncludeNameAndSurname() {

Person person = new Person("Vilkas", "Pilkas");

String expected =

"Person[" + person.getName() + " " + person.getSurname() + "]"

assertEquals(expected, person.toString());

}

Page 40: Developer Test - Things to Know

Anti-pattern: The Ugly Mirror@Test

public void personToStringShouldIncludeNameAndSurname() {

Person person = new Person("Vilkas", "Pilkas");

String expected =

"Person[" + person.getName() + " " + person.getSurname() + "]"

assertEquals(expected, person.toString());

}

Page 41: Developer Test - Things to Know

Anti-pattern: The Ugly Mirror@Test

public void personToStringShouldIncludeNameAndSurname() {

Person person = new Person("Vilkas", "Pilkas");

assertEquals("Person[Vilkas Pilkas]", person.toString());

}

Page 42: Developer Test - Things to Know

Thing #7How to turn off the test?

Page 43: Developer Test - Things to Know

Why would you want to turn off the test?

● Well, because it fails… :)

Page 44: Developer Test - Things to Know

Ignoring tests

● Always use ignore/pending API from your test library (JUnit @Ignore)

Page 45: Developer Test - Things to Know

Ignoring tests

● Always use ignore/pending API from your test library (JUnit @Ignore)

● Do not comment out or false assert your test

Page 46: Developer Test - Things to Know

Ignoring tests

● Always use ignore/pending API from your test library (JUnit @Ignore)

● Do not comment out or false assert your test● If you do not need a test - delete it

Page 47: Developer Test - Things to Know

Thing #8What to do with exceptions?

Page 48: Developer Test - Things to Know

Exceptions

● If you can, use matchers instead of○ @Test(expected=?)

Page 49: Developer Test - Things to Know

JUnit expected exception@Test(expected=IndexOutOfBoundsException.class)public void shouldThrowIndexOutOfBoundsException() { ArrayList emptyList = new ArrayList(); Object o = emptyList.get(0);}

//matcher in Specs2 (Scala)

server.process(None) must throwA[NothingToProccess]

Page 50: Developer Test - Things to Know

Exceptions

● If you can, use matchers instead of○ @Test(expected=?)○ try-catch approach

Page 51: Developer Test - Things to Know

try and catch

public void shouldThrowIndexOutOfBoundsException() { ArrayList emptyList = new ArrayList();

try { Object o = emptyList.get(0);

fail("Should throw IndexOutOfBoundsException");

} catch(IndexOutOfBoundsException e)){

//consider asserting message!

}}

Page 52: Developer Test - Things to Know

Exceptions

● If you can, use matchers instead of○ @Test(expected=?)○ try-catch approach

● catch-exception lib

Page 53: Developer Test - Things to Know

catch-exception libList myList = new ArrayList();

catchException(myList).get(1);

assertThat(caughtException(),

allOf(

is(IndexOutOfBoundsException.class),

hasMessage("Index: 1, Size: 0"),

hasNoCause()

)

);

Page 54: Developer Test - Things to Know

Exceptions

● If you can, use matchers instead of○ @Test(expected=?)○ try-catch approach

● catch-exception lib● What about ExpectedException Rule?

○ My personal opinion - not that intuitive○ breaks arrange/act/assert flow

Page 55: Developer Test - Things to Know

ExpectedException rule@Rule public ExpectedException exception = ExpectedException.none();

@Testpublic void testExpectedException() { exception.expect(IllegalArgumentException.class); exception.expectMessage(containsString('Invalid age')); new Person('Vilkas', -1);}

//Person constructor

public Person(String name, int age) { if (age <= 0) throw new IllegalArgumentException('Invalid age:' + age);

// ...

}

Page 56: Developer Test - Things to Know

Thing #9How to test asynchronous code?

Page 57: Developer Test - Things to Know

Asynchronous code

● Do not Thread.sleep - makes test slow

Page 58: Developer Test - Things to Know

Asynchronous code

● Do not Thread.sleep - makes test slow● Use Awaitility, or similar DSL for

synchronizing asynchronous operations

Page 59: Developer Test - Things to Know

Awaitility (Java 8 example)@Testpublic void shouldPersistNewUser() { publish(new CreateUserCommand("Vilkas Pilkas"));

await().until(userRepo::size, is(1));

//how long to await? (Default is 10 seconds) await().until(userRepo::isNotEmpty);}

Page 60: Developer Test - Things to Know

Asynchronous code

● Do not Thread.sleep - makes test slow● Use Awaitility, or similar DSL for

synchronizing asynchronous operations● Use reasonable await time to avoid flaky

tests

Page 61: Developer Test - Things to Know

Thing #10Testing with time

Page 62: Developer Test - Things to Know

Problempublic class MyService {

...

public void process(LocalDate date) { if (date.isBefore(LocalDate.now()) { ... } }

}

Page 63: Developer Test - Things to Know

Testing with Time

● Design your system where time is a collaborator

● Inject test specific time provider in your test○ constant time○ slow time○ boundary cases time

Page 64: Developer Test - Things to Know

Control time with Clockpublic class MyService { private Clock clock; // dependency inject

...

public void process(LocalDate date) { if (date.isBefore(LocalDate.now(clock)) { ... } }

}

Page 65: Developer Test - Things to Know

Thing #11Collections

Page 66: Developer Test - Things to Know

Collections - multiple properties to assert

● Is null?● Size● Order● Content

Page 67: Developer Test - Things to Know

Collections● Most of the time you want to assert on collection content

Page 68: Developer Test - Things to Know

Collections● Most of the time you want to assert on collection content● Prefer exact content matching

Page 69: Developer Test - Things to Know

Collections● Most of the time you want to assert on collection content● Prefer exact content matching● Avoid incomplete assertions

Page 70: Developer Test - Things to Know

Collections● Most of the time you want to assert on collection content● Prefer exact content matching● Avoid incomplete assertions● Do not sort just because it is easier to assert!

Page 71: Developer Test - Things to Know

Collections● Most of the time you want to assert on collection content● Prefer exact content matching● Avoid incomplete assertions● Do not sort just because it is easier to assert!● Multiple assertions are worse than single content

assertion

Page 72: Developer Test - Things to Know

Collections● Most of the time you want to assert on collection content● Prefer exact content matching● Avoid incomplete assertions● Do not sort just because it is easier to assert!● Multiple assertions are worse than single content

assertion● Unless you want to say something important in your test!

Page 73: Developer Test - Things to Know

Collections● Most of the time you want to assert on collection content● Prefer exact content matching● Avoid incomplete assertions● Do not sort just because it is easier to assert!● Multiple assertions are worse than single content

assertion● Unless you want to say something important in your test!● Use matchers!

Page 74: Developer Test - Things to Know

Thing #12Access modifiers

Page 75: Developer Test - Things to Know

Access modifiers

● Rule is simple - never access anything that is not public in your tests

Page 76: Developer Test - Things to Know

Access modifiers

● Rule is simple - never access anything that is not public in your tests

● Private things are implementation details which are not part of the public contract

Page 77: Developer Test - Things to Know

Access modifiers

● Rule is simple - never access anything that is not public in your tests

● Private things are implementation details which are not part of the public contract

● Same applies for protected/package modifiers. They must be there for production code, but not available to your tests

Page 78: Developer Test - Things to Know

Thing #13Random values

Page 79: Developer Test - Things to Know

Random values in tests

● Most of the time you do not want it

Page 80: Developer Test - Things to Know

Random values in tests

● Most of the time you do not want it● Unless you depend on randomness a lot (eg.

password generation*)

*Thanks to Aleksandar Tomovski for a good example

Page 81: Developer Test - Things to Know

Random values in tests

● Most of the time you do not want it● Unless you depend on randomness a lot● Use property based testing (which is also

hard)

Page 82: Developer Test - Things to Know

Random values in tests

● Most of the time you do not want it● Unless you depend on randomness a lot● Use property based testing (which is also

hard)● Do not make dummy values random

Page 83: Developer Test - Things to Know

What if we still need random cases?

Page 84: Developer Test - Things to Know

Generate Multiple Test Cases

● Quality over quantity

Page 85: Developer Test - Things to Know

Generate Multiple Test Cases

● Quality over quantity● Think of boundary cases, that you may want

to detect with random test

Page 86: Developer Test - Things to Know

Generate Multiple Test Cases

● Quality over quantity● Think of boundary cases, that you may want

to detect with random test● Use parameterized tests

Page 87: Developer Test - Things to Know

Generate Multiple Test Cases

● Quality over quantity● Think of boundary cases, that you may want

to detect with random test● Use parameterized tests● Random is hard to repeat

Page 88: Developer Test - Things to Know

Generate Multiple Test Cases

● Quality over quantity● Think of boundary cases, that you may want

to detect with random test● Use parameterized tests● Random is hard to repeat● Flickering tests

Page 89: Developer Test - Things to Know

Thing #14How many assertions per test?

Page 90: Developer Test - Things to Know

How many assertions per test?

● Unit test - one assertion per test. Must be clear and readable

● Proper unit tests should fail for exactly one reason

● End to end - best case one assertions per test, but more allowed

● Consider custom matchers

Page 91: Developer Test - Things to Know

Thing #15Test Doubles

Page 92: Developer Test - Things to Know

Test Doubles

The name comes from the notion of a Stunt Double in movies

Page 93: Developer Test - Things to Know

Why do we need test doubles?

● To test in an isolated environment by replacing real collaborators with doubles

● To have fast tests● To test interactions● To change collaborators behaviour in test

Page 94: Developer Test - Things to Know

Types of Test Doubles

● Dummy● Fake● Stub● Spy● Mock

Page 95: Developer Test - Things to Know

Dummy

Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists

Page 96: Developer Test - Things to Know

Fake

Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example)

Page 97: Developer Test - Things to Know

Stub

Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'

Page 98: Developer Test - Things to Know

Spy

Stubs that verify behavior

Page 99: Developer Test - Things to Know

Mock

Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive

Page 100: Developer Test - Things to Know

So what’s the difference between Spy and Mock?

Page 101: Developer Test - Things to Know

Thing #16Naming

Page 102: Developer Test - Things to Know

Where to keep your tests

● Recommendation: keep them in separate package from production code

Page 103: Developer Test - Things to Know

Class Naming

● End test classes with Test suffix (or one required by your test framework)

● For long test classes:○ split by feature○ by input type

Page 104: Developer Test - Things to Know

Method Naming

● Everybody should follow the same pattern● testGeneratePassword vs

shouldGenerateValidPassword

Page 105: Developer Test - Things to Know

Naming Test Doubles

● Intention revealing stubEngine, spyServer, dummyUser, mockService

Page 106: Developer Test - Things to Know

Thing #17Comments

Page 107: Developer Test - Things to Know

Comments in Test code

● Fundamentally good option to explain complicated parts, but:

● better use good method naming● custom matcher● do less, so that intention is clear● comments are not so bad in isolated well

named set up method (not the first thing to be seen in test method)

Page 108: Developer Test - Things to Know

Thanks!Q & A