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
?