This workshop is about testing the right way. Get a clear view on how to test your code in an efficient and useful way!
This first testing-related workshop is about all aspects of unit testing. Integration testing and TDD will have their own dedicated workshops.
5. 100% Code Coverage
Test coverage is a useful tool for finding untested parts of a codebase.
Test coverage is of little use as a numeric statement of how good your
tests are.
-- Martin Fowler
6. public static String foo(boolean someCondition){
String bar = null;
if (someCondition) {
bar = "blabla";
}
return bar.trim();
}
100% Code Coverage
assertEquals("blabla", foo(true));
assertEquals("blabla", foo(false));
Line Coverage 100%
Bug Coverage 0%
7. Reduce Costs
Most of our time is spent on debugging and fixing bugs.
Bugs - if not fixed soon in the development process - cost a lot more than
the development itself
Google est. bug cost when fixed at:
Development time $5
Automated build time $50
Integration testing time $500
System testing time $5000
9. Tests as documentation
Method names are important to keep tests readable
While a very subjective topic, a good practice can be:
dividesAmountByFactor
throws_Illegal_Argument_Exception_When_Dividing_By_Zero
throws_exception_when_dividing_by_zero
private double amount;
public double divide(double factor){
if (factor == 0) throw new IllegalArgumentException();
return amount/factor;
}
12. Tests make you think about your implementation
▪ Tests often trigger refactors and even redesigns
▪ Tests make us really think about the requirements
▪ Tests make us find gaps in the requirements
13. Conclusion
Write unit tests to…
… reduce costs / save time
… be able to confidently refactor
… look at your technical design from a different angle
… look at your requirements from a different angle
19. Test isolated units
No knowledge of implementation details of called method of collaborator
20. Test isolated units
How ?
Extensive use of mocked-out collaborators in non-leaf objects/classes
21. Test isolated units
It results mostly in one test-class per class
e.g: MyClass and MyClassTest
22. Test isolated units
Advantage:
Less tests to write
Smaller tests to write
Less complicated setup of fixtures
Probably all branches covered quickly
23. Boundary Cases
Test cases that cover the full spectrum of possible values.
Example 1:
A method that accepts only int values between 0 and 100
public void compute(int value){}
Test with -1, 0, 10, 50, 100, 101, 'A'
24. Boundary Cases
Example 2:
A method that removes a character in a String text
public String remove(String text, char ch)
1. Test for null text
2. Test for empty text
3. Test for character which is not in String
4. Test for characters which comes during start, end or middle of String
5. Test to cover if text String contains just one character which is equal or not equal to the
to be removed one
6. Test with String contains just one character multiple times
25. Branches
Every time that a different path of execution can be triggered results in a
different branch
Basically: if, else, for, while, do blocks and even collaborators
if(condition){
}
2 branches : if true and if false
27. Branches: Cyclomatic complexity
Start with a count of one for the method. Add one for
each of the following flow-related elements that are
found in the method.
Methods Each return that isn't the last statement of a method
Selection if, else, case, default
Loops for, while, do-while, break, and continue
Operators &&, ||, ?, and :
Exceptions catch, finally, throw, or throws clause
Threads start() call on a thread. Of course, this is a ridiculous underestimate!
28. Branches: Cyclomatic complexity
If this number is higher than 10 it becomes nearly impossible to test.
“Impossible to test” = impossible to have full branch coverage
33. Code Coverage: Tools
JaCoCo: replacement for Emma, fully supports Java 7 and 8, used by
Sonar, EclEmma(used to be based on EMMA), Jenkins, Netbeans,
IntelliJ IDEA, Gradle
Clover: Atlassian → commercial
Cobertura
34. Conclusion
What to test :
▪ Completely in isolation
▪ All Boundary cases
▪ All branches
Be aware of :
▪ Cyclomatic complexity
▪ SonarQube calculates it out of the box for you
▪ Branch coverage
▪ Calculation possible by
▪ Jacoco, Emma, Clover, Cobertura
37. Definition (non-software)
A Fixture in non-software development is e.g.: the setup of a controlled
environment to test the functioning of a piece of hardware
38. Definition (software)
A fixed state of software under test used as baseline for running tests
= test context
Preparation of input data and setup/creation of fake or mock objects
43. ObjectMother
Factory for creating fixtures that are used in several test classes.
Catchy name thought of at thoughtworks
http://martinfowler.com/bliki/ObjectMother.html
44. ObjectMother
private PsychoKiller hanibal;
@Before
public void setupFixtureForHanibalLastKill2DaysAgo(){
hanibal = new PsychoKiller();
hanibal.setFirstName(“Hanibal”);
hanibal.setLastName(“Lecter”);
hanibal.setLastKillDate(current - 2 days);
}
45. ObjectMother
private PsychoKiller hanibal;
@Before
public void setupFixtureForHanibalLastKill2DaysAgo(){
hanibal = PsychoKillerMother.
getHanibalLastKill2DaysAgo();
}
@Test
public void returns_true_if_less_than_4_days(){
assertTrue(hanibal.killedRecently());
}
46. Conclusion
▪ Use Fixtures to reduce code duplication
▪ In JUnit 4 : @Before @BeforeClass @After @AfterClass
▪ ObjectMother: to help keeping setup of fixtures small and concise and
in one place so reusable for other test-classes
60. Dummy
Dummy objects are passed around but never actually used. Usually they
are just used to fill parameter lists
61. POJO Dummy
public class TestMockDummy {
final Engine dummyEngine = new DieselEngine();
Car testedCar = new Car(new DieselEngine());
@Test(expected = IllegalStateException.class)
public void testExceptionIfCarStarted() {
testedCar.start();
testedCar.setEngine(dummyEngine);
}
}
62. Fake
Fake objects have working implementations, but usually take some
shortcut which makes them not suitable for production
63. POJO Fake
private final Engine fakeEngine = new Engine() {
boolean started=false;
@Override
public void stop() { started=false; }
@Override
public void start() { started=true; }
@Override
public boolean isRunning() { return started; }
};
64. POJO Fake (cont)
…
private final Car carWithStartableEngine = new Car(fakeEngine);
@Test
public void testCarCanStart() {
carWithStartableEngine.start();
Assert.assertTrue(carWithStartableEngine.isStarted);
}
…
65. Stubs
Stubs provide canned answers to calls made during the test, usually not
responding at all to anything outside what's programmed for the test.
66. POJO Stubs
private final Engine stubEngine = new Engine() {
@Override
public void stop() {}
@Override
public void start() {}
@Override
public boolean isRunning() { return false; }
};
67. POJO Subs (cont)
private final Car car = new Car(stubEngine);
@Test
public void testCarDoesNotStartWithoutEngine() {
car.start();
Assert.assertFalse(car.isStarted);
}
69. POJO Spies
private class SpyEngine extends DieselEngine {
int startedCalledCount = 0;
@Override
public void start() {
startedCalledCount++;
super.start();
}
};
70. POJO Spies (cont)
private final SpyEngine spyEngine = new SpyEngine();
private final Car spiedTestCar = new Car(spyEngine);
@Test
public void testCarStartStartsTheEngine() {
spiedTestCar.start();
Assert.assertEquals("Engine start is called once",
1, spyEngine.startedCalledCount);
}
72. EasyMock: Mock (too lazy)
@RunWith(EasyMockRunner.class)
public class TestMockEasyMock
extends EasyMockSupport{
@Mock
Engine mockEngine;
…
73. EasyMock: Mock (cont)
@Test
public void testCarWithAMockedEngine() {
// Set the behavior
expect(mockEngine.isRunning()).andReturn(false);
mockEngine.start();
expect(mockEngine.isRunning()).andReturn(true);
mockEngine.stop();
replayAll();
74. EasyMock: Mock (cont 2)
// Set a car with our mocked engine
Car testedCar = new Car(mockEngine);
// Run !
testedCar.start();
testedCar.stop();
// Verify !
verifyAll();
}
76. (Some) Existing frameworks
▪ EasyMock
▪ Mockito
▪ Unitils
▪ JMockit
And Much Much more !
Proxy Based
Class remap
Strict by default
Non-Strict by
default
80. Unitils (mocking)
1. Arrange (Create the mock and setup behavior)
myServiceMock =
new MockObject<MyService>(MyService.class, this);
myServiceMock.returns("a value").someMethod();
2. Act
myServiceMock.getMock().someMethod();
3. Assert !
myServiceMock.assertNotInvoked().someMethod();
81. JMockit - Expectations
@Test
public void aTestMethod(@Mocked final MyCollaborator mock){
new NonStrictExpectations() {{
mock.getData(); result = "my test data";
mock.doSomething(anyInt, "some expected value", anyString);
times=1;
}};
// In the replay phase, the tested method would call the "getData" and "doSomething"
// methods on a "MyCollaborator" instance.
...
// In the verify phase, we may optionally verify expected invocations to
// "MyCollaborator" objects.
...
}
82. JMockit - Verification
...
new Verifications() {{
// If no new instance of the mocked class should have been
// created with the no-args constructor, we can verify it:
new MyCollaborator(); times = 0;
// Here we verify that doSomething() was executed at least once:
mock.doSomething();
// Another verification, which must have occurred no more than three
// times:
mock.someOtherMethod(
anyBoolean, any, withInstanceOf(Xyz.class)); maxTimes = 3;
}};
}
87. Assertions
How ?
Should be automated, no human intervention necessary
No logging, System.out.println() etc. to be used
Use framework(s) to achieve this.
89. Junit assertions
Drawbacks
▪ Not always very readable
assertEquals(expected, result) or assertEquals(result, expected)
▪ Number of assertions are limited
▪ Comparing (complex) objects is hard.
impossible when object has not equals() implemented !
Conclusion
It’s ok to use them for primitive types.
But there are better alternatives.
90. Hamcrest assertions
What ?
▪ Hamcrest is a framework for writing matcher objects allowing ‘match’
rules to be defined declaratively.
assertThat(Object, Matcher<T>);
▪ Can easily be integrated with other frameworks like Junit, TestNG,
Mockito, EasyMock, Jmock,...
91. Hamcrest assertions
Advantages
▪ Improved readability of tests
assertThat(ObjectToBeChecked, equalTo(OtherObject))
assertThat(ObjectToBeChecked, is(equalTo(OtherObject));
assertThat(collection, hasSize(2);
▪ Better failure message
assertThat(3, greaterThan(5));
Expected: a value greater than <5>
but: <3> was less than <5>
92. Hamcrest assertions
▪ Combination of Matchers
Allows to assert more precisely.
assertThat(array, not(emptyArray());
assertThat(collections, everyItem(greaterThan(10));
▪ Write your own Matcher
This can occur when you find a fragment of code that test the same set of properties over and over
again and you want to bundle the fragment into a single assertion.
But be aware, there are already plenty of matchers available, make sure you are not writing existing
code again.
93. Hamcrest assertions
Example :
To test if a double has a value NaN (not a number)
Test we want to write :
public void testSquareRootOfMinusOneIsNotANumber () {
assertThat(Math.sqrt(-1), is(notANumber()));
}
94. public class IsNotANumber extends TypeSafeMatcher<Double> {
@Override
public boolean matchesSafely(Double number) {
return number.isNaN();
}
public void describeTo(Description description) {
description.appendText("not a number");
}
@Factory
public static <T> Matcher<Double> notANumber() {
return new IsNotANumber();
}
}
Hamcrest assertions
95. Hamcrest assertions
▪ More possibilities to compare objects
It is possible to check objects that don’t have equals() implemented.
assertThat(ObjectToBeChecked, samePropertyValuesAs(OtherObject));
but not possible for objects with composition !
Better use ReflectionAssert.assertReflectionEquals of unitils !
96. Hamcrest assertions
Drawbacks
▪ Finding the right Matcher
The matchers are not set in one place. Most matchers are accessible via the Matcher class,
but some are located in the CoreMatcher class, and some are in another package.
example:
hasItem() : Matcher class
hasItems() : IsCollectionContaining class
97. Other frameworks
Unitils
▪ ReflectionAssert
This assertion loops over all fields in both objects and compares their values using reflection. If a
field value itself is also an object, it will recursively be compared field by field using reflection. The
same is true for collections, maps and arrays.
▪ Lenient assertions
Adding some levels of leniency to the ReflectionAssert checks. (order list, ignoring defaults, dates,
assertLenientEquals)
▪ Property assertions
Methods to compare a specific property of two objects
assertPropertyLenientEquals("id", 1, user);
assertPropertyLenientEquals("address.street", "First street", user);
99. Assertions
Misuse of assertions
▪ Manual assertions
This practice misses out the main benefits of testing automation —
the ability to continuously run the tests in the background without intervention
■ Multiple assertions
■ Redundant assertions
Extra calls to an assert method where the condition being tested is a hard coded value
assertTrue(“always true”, true)
■ Using the wrong assertions
assertTrue("Object must be null", actual == null);
assertTrue("Object must not be null", actual != null);
100. Assertions
What about void methods ?
Often if a method doesn't return a value, it will have some side effect. There may be a way to verify
that the side effect actually occurred as expected.
Especially exception testing should not be forgotten.
101. Assertions
MyClass {
public void addElement(String element, List<String> elements) {
elements.add(element);
}
}
public void testAddElement() {
List<String> elements = new ArrayList();
assertEquals(0, elements.size() );
myClassTest.addElement(“test”, elements);
assertEquals(1, elements.size() );
}
102. Conclusion
▪ Always make sure your assertions are fully automated
▪ Junit assertions are ok for primitive types
▪ Hamcrest offers a lot of interesting matchers that allows you to assert
more precise
▪ Unitils is a better alternative when comparing objects
▪ You can use them all together !
103. The Red Line
Goal of Testing
What to Test
Fixtures
Mocks
Assertions
108. Factoid - Provider
Provider interface:
▪ Provides a list of fact to a client
▪ int size(): Number of facts in the provider
▪ Fact getFact(index): Return a fact (0->size-1)
▪ Implementation:
▪ FileFactProvider
▪ Loads facts from a file.
▪ Line oriented
109. Factoid- Service
FactService interface:
▪ Returns a fact to client:
▪ Fact getAFact(): Return a fact
▪ Implementation:
▪ RandomFactService:
▪ Returns a random fact using a provider
▪ Uses Random
▪ Builds an array to avoid repetition/ensure all facts are returned
110. Factoid - Main
Factoid main class
▪ Loads a File with a (File)FactProvider
▪ Loads a (Random)FactService using created fact provider
▪ Calls FactService getAFact()
111. Factoid - What to do
▪ Select the mocking framework you want
▪ EasyMock to start !
▪ Check the FIXME in the existing code
▪ Fix as many as you can !
▪ Be creative
▪ Look for other issues ;-)