test driven development - junit basics and best practices

Post on 22-Jan-2018

1.163 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

TEST DRIVEN DEVELOPMENTUnit testing basics and best practices

Let’s get test infected!

@npathai on github

Narendra Pathai5 years of core development experience

4 years with unit testing2 years with TDD

Collaborator of Java Design Patterns repositoryContributor to JUnit 4 and Java Hamcrest

@harshSE on github

Harsh Patel5 years of core development experience

3 years with unit testing1 years with TDD

Contributor to JMeter, Java Design Patterns

“TESTING CAN BE USED TO SHOW presence of bugs,

NOT absence”- Edsger Dijkstra

myths & misconceptions

Let’s write unit test cases because then there will be

no bugsYayyy!!

Myth 1

less bugsIf unit testing is done properly then there will be

Reality 1

wastes timeI would rather write something that is useful because it

Myth 2

saves timeI must write tests today so that it

tomorrow

Reality 2

finding bugsUnit testing is just about

Myth 3

Multi-faceted unit testsdrive code design

executable specification

Reality 3

100% coverageI can be confident that code works only if there is

Myth 4

n% coverageIt’s perfectly fine to have

if it gives the required confidence in the code

Reality 4

production codeDevelopers only write

Myth 5

QA team does the testing

unit testsIt’s developers who must write

Reality 5

What is

UNIT TESTING?

tool { It’s a

DESIGN

CONFIDENCE

REGRESSION

TESTS ARE FIRST CLIENTS

OF CODE

TESTS ARE FIRST CLIENTS

OF CODE

I repeat

TESTPYRAMID

UNIT TESTS

INTEGRATION TESTS

END 2 END TESTS

MANUAL TESTS

CONF

IDENC

E IN

ENTIR

E SYS

TEM

CONF

IDENC

E IN

INDIV

IDUAL

CHAN

GE

EXEC

UTIO

N TIM

E

RELIA

BILITY

High

Low

Low

High

COST

ICE CREAM CONE

Inverse of Test Pyramid

More manual than unit tests

Too costly to test

what we don’t want

Difficult to do in depth testing

Often how testing is done

UNIT TESTS

INTEGRATION TESTS

END 2 END TESTS

MANUAL TESTS

WHO WILL WRITE WHICH TESTS?

Not important right now, a topic for some other day!

NAMINGCONVENTIONYou should call a spade a spade

HOW TO NAME TEST METHODS

UnitOfWork_StateUnderTest_ExpectedBehavior

behavior under test

define current state of object Resultant state / event

HOW TO NAME TEST METHODS

poppingAStack_whenEmpty_throwsException

popping from stack

when stack is emptybecause empty stack cannot be

popped

TEST METHODS ON STEROIDS

poppingAnEmptyStackThrowsException

popping from stack

when stack is emptybecause empty stack cannot be

popped

BUILD THE RIGHT THING

poppingAnEmptyStackThrowsException {Object obj = stack.pop();

}

are we building the right thing?

are we building it right?

Writing the first

TEST

xUnit frameworksare language specific

Java

jUnit

.NET

nUnit

C++

CppTest

Javascript

qUnit*multiple frameworks per language are available

@Test is the way to define a new test case

@Before setup environment to run each test

static Don’t even think about it.

@After cleanup resources after each test case

@BeforeClass setup expensive resources shared across all tests

Think static

@AfterClass cleanup expensive resources shared across all tests

assertionA statement that is expected to be true at that point in code

assertionPassing

test passes

|assertionFailing

test fails

assert keywordassert 2 + 1 == 3;

Limited powerLower expressiveness

jUnit assertion More power

* Better expressiveness

assertXXX(...) methods

assertTrue(“woah!”, 2 + 1 == 3);

assertion à la jUnit

assertEquals(3, 2 + 1);

assertArrayEquals([], []);

AARRANGE

AACT

AASSERT

(given) (when) (then)

Defining a UNITthere is no correct way

UNIT is SUBJECTIVEa single class

a group of related classes

When defining a UNITDON’T CONSIDER INTERNAL

STRUCTURE OF CLASS AS A UNIT

IT TAKES EXPERIENCE

TRIAL & ERROR

now introducing

Hamcrest Matchersassertion on STEROIDS

assertThat(2 + 1, equalsTo(3));

expressive assertions

assertThat(list, hasSize(3));

assertThat(list, contains(“a”));

assertThat(person, hasAge(30));

custom assertions

assertThat(2, isEven());

assertThat(21, isDivisbleBy(3));

unexpectedEXCEPTIONSnot all exceptions are unexpected

@TestpoppingAnEmptyStackThrowsException() {

try {... fail();} catch (SomeException e) {assertThat(e.getMessage(), ..);

}}

Using try...catch

Error prone, we can do much better

@Test(expected = EmptyStackException.class)poppingAnEmptyStackThrowsException()

Using @Test

but cannot test exception message!

follow | RULESJUNIT

ADDITION OR REDEFINITION OF TEST METHOD BEHAVIOR

ExpectedException rule@Rule public ExpectedException thrown =

ExpectedException.none();

declares a rule reference rules must be public

poppingAnEmptyStackThrowsException() {thrown.expect(StackEmptyException.class);thrown.expectMessage(“stack is empty”);

emptyStack.pop();}

ExpectedException rule

@Rule public TemporaryFolder folder = new TemporaryFolder();

folder.newFile(“myFile.txt”);folder.newFolder(“subFolder”);

TemporaryFolder ruleAutomatically deletes temp files and directories

ExternalResource

Timeout

Custom

More rules!No more leaked resources

Fail tests after timeout

Define custom rules

Run with (Runners)

way to customize test processing & extend its purpose.

@RunWith A class level annotation that selects the runner to use for test case@RunWith(Suite.class)

class of the runner to use

Hierarchical runnerorganize scenarios of the unit with nested classes/contexts

Suite runnerorganize several feature tests of a unit together

data >> teststest a system using data inputs

JUnitParams library

Data driven testing from multiple types of sources@RunWith(JUnitParamsRunner.class)

PASS FROM ANNOTATION

@Parameters({“a, A”, “b, B”})@TesttoUpperCaseConvertsCharactersToUpperCase(String in ,

String expectedOut)

PASS FROM METHOD

@Parameters(method=”dataFor_toUpperCase”)@TesttoUpperCaseConvertsCharactersToUpperCase(String in ,

String expectedOut)

PASS FROM CLASS

@Parameters(source = DataForToUpperCase.class)@TesttoUpperCaseConvertsCharactersToUpperCase(String in ,

String expectedOut)

PASS FROM FILE

@FileParameters(“dataForToUpperCase.csv”)@TesttoUpperCaseConvertsCharactersToUpperCase(String in ,

String expectedOut)

DON’T DO THIS AT HOME

DIFFERENT SCENARIO - SAME DATA SOURCE

DRIVING CONDITIONS FROM DATA

and the tools change usTOOLS

We change the

EclEmma Plugina plugin for code coverage in eclipse

Infinitest Plugina plugin that runs tests continuously in eclipse

Anti-PatternsEvery time you do this a kitten dies…

1TEST CODE IS SECONDARY

Anti-Pattern

1PRODUCTION code

=TEST code

Best Practice

2

REPEATINGTEST CODE

Anti-Pattern

2

KEEP CODE DRY

Best Practice

3

TESTINGinternalsor third party code

Anti-Pattern

3TEST USING PUBLIC API

Best Practice

4

MULTIPLE SCENARIOS in a test

Anti-Pattern

4SINGLE

SCENARIOper test

Best Practice

5

CHANGE OR DELETEtests*

Anti-Pattern

5ONLY IF

CHANGE IN behavior

Best Practice

Ending thoughtswe have learnt a lot!

PRODUCTION CODE TEST CODE

PRODUCTION CODE TEST CODE

contains nesting doesn’t contain nesting

Is mostly imperative Is declarative

takes more time to write

takes less time to write

?

top related