SlideShare une entreprise Scribd logo
Effective Unit Testing
Objects, Dependencies and Mocks
@npathai on GitHub
Narendra Pathai
Developing high performance systems,
Software Craftsman, Trainer & Mentor
Unit Test = Test per class
We can choose as small or as large a unit
/
Coverage = Good Tests
If there is no assertion, coverage is of no use!!!
/
Unit Testing = Slow Development
We fail constantly and are extremely slow
when learning anything, does it mean we are
still slow?!
/
No place for excuses
Think of learning to walk, talk, typing!
Must see test case failing once
If a test case hasn’t failed even once, how do
you know it is EVEN checking something?
Web of Objects
Web of Objects
● Communication patterns are set of rules
● Rules govern roles they play, what messages they
can send and when
● Communication patterns gives meaning to possible
relationships among objects
Web of Objects
● Either the object returns value to the caller
● Either the object changes it’s state
● Either it interacts with other object via message
passing
● Makes an external call that changes state of that
system such as database call, web service call etc.
Calculator Display
add(int, int)
subtract(int, int)
show(int)
clear()
currentValue()
calls
SYSTEM UNDER
FOCUS
COLLABORATOR
INTERACTIONS
calls
int int
MESSAGES
CLIENT
Web of Objects
● set.add(val)
○ Adds value to the set (change of state)
○ Returns boolean (message passing)
● list.clear()
○ Clears all elements (change of state)
○ Doesn’t return any value (no message passing)
○ Need to call additional methods to verify
behavior eg. size(), isEmpty()
Web of Objects
● observable.notifyObservers()
○ Neither returns any value (no message
passing)
○ Nor does it change any state
○ It interacts with other objects/observers and
passes message to those objects
Testing Units in Isolation
TEST
TEST
?
?
?
?
Mocks
Seam
TEST
Real
Unit under test
Internal
Structure
Finding relationship
between objects
Dependencies/
Collarborators
Message passing/
Notification
Verification Strategies
STATE
VERIFICATION
BEHAVIOR
VERIFICATION
Verification Strategies
STATE
VERIFICATION
BEHAVIOR
VERIFICATION
State Verification
A
B
C
SETUP
ACT
VERIFY
TEARDOWN
Exercise
System Under
Test (SUT)
sut.method(..)
Get State
sut.getState()
!
?
Indirect
Output
Real
Mock
Collaborators
Credit: xUnit Test Patterns
State Verification
ArrayList<Integer> list = new ArrayList<>();
// Exercise the system under test
list.add(1);
// State verification - verify list is no
more empty
assertFalse(list.isEmpty());
State Verification
// Create a new thread
Thread t = new Thread(() -> {});
// State verification - Verify it is in NEW
state
assertThat(t.getState(),
is(equalTo(Thread.State.NEW)));
Verification Strategies
STATE
VERIFICATION
BEHAVIOR
VERIFICATION
Behavior Verification
● How to test if SUT does not have any state?
● How to test if SUT does not change state?
Behavior Verification
When SUT does not change state or return value or
cause any visible side effect, it MUST interact with
other object or component.
We can test that interaction!
Behavior Verification
A
B
C
SETUP
ACT
VERIFY
TEARDOWN
Exercise
SUT
sut.method(..)
?
Indirect
Output
Real
Mock
Collaborators
!
!
!
Seam Credit: xUnit Test Patterns
● Places where we can alter system flow
● Essentially polymorphism (ideally interfaces)
● Allow us to isolate SUT when unit testing
● Procedural code (static) has no seams
● Difficult to test procedural code
SEAM
Not all procedural code is bad! Eg. static utilities like
Math.abs(), Math.max() etc.
Behavior Verification
WeatherObservable observable = new WeatherObservable();
WeatherObserver observer = new WeatherObserver();
observable.addObserver(observer);
// exercise the SUT
observable.setWeather("FALL");
// Verify that observer was notified
// How to verify that?
Behavior Verification Strategies
MANUAL
VERIFICATION
MOCKING
LIBRARY
Behavior Verification Strategies
MANUAL
VERIFICATION
MOCKING
LIBRARY
Manual Behavior Verification
// A test specific observer that tracks if method
was called
class TestObserver implements Observer {
private boolean called = false;
@Override
public void update(Observable o, Object arg) {
this.called = true;
}
}
Manual Behavior Verification
WeatherObservable observable = new WeatherObservable();
// Custom hand rolled observer
TestObserver observer = new TestObserver();
observable.addObserver(observer);
// exercise the SUT
observable.setWeather("FALL");
// Manual behavior verification
assertTrue(observer.called);
Manual Behavior Verification
How to test the following:
● Observer should only be called once
● Observer should be called with exact argument
Manual Behavior Verification
// A test specific observer that tracks if method
was called
class TestObserver implements Observer {
private int count = 0;
private Object arg;
@Override
public void update(Observable o, Object arg) {
this.count++;
this.arg = arg;
}
}
Manual Behavior Verification
// Manual behavior verification
// Test Readability suffering
assertEquals(1, observer.count);
assertEquals("FALL", observer.arg);
Manual Behavior Verification
How to test the following:
● Any one of multiple methods should be called
● Different methods called with different arguments
● Observers should be called in exact order in which
they were added
● Many more advanced verifications..
Manual Behavior Verification
We are already starting to add logic in test code
itself!! So there is a possibility that test code has
bugs.
Me: So now should we write test cases for test
code?!
Your reaction: That ain’t happening!
Behavior Verification Strategies
MANUAL
VERIFICATION
MOCKING
LIBRARY
Behavior Verification Strategies
1. Complicates test logic
2. Need to repeat it for
all classes
3. Time consuming and
error prone
4. Test readability
suffers
MOCKING
LIBRARY
Behavior Verification Strategies
MANUAL
VERIFICATION
MOCKING
LIBRARY
Features
STUBBING
EXPECTATION
VERIFICATION
Stubbing
● Providing hard-coded
answers/behaviors from
methods called during test
● Test case wise
answer/behavior can be
changed
● Stubbing logic MUST be
simple
For instance:
● Returning a test/dummy user
from DAO layer
● Throwing exception from
DAO layer to replicate error
scenario
a.k.a Training phase
Exp. Verification
● Verify how the SUT should
have interacted with its
collaborators
● Verify the communication
contract between SUT and its
collaborators
● MUST be present in every
test case
For instance:
● Verify Observer#update()
method was called once only
● Verify argument was
matching
● Verify Observers were called
in order they were added
a.k.a assertion phase
Mocks
Stubs
Stubbing + Verification
Hard-coded results
No verification
Fakes
Working but simplified version of
production code, like DAO using
collection rather than DB
Dummy
Objects passed around but never
actually used
Fake
a working simplified version
LoginService
AccountDAO
FakeAccountDAO HashMap
Fake
implementation
login(user)
getPasswordHash()
DB
Stub
object trained with predefined data
GradesService Gradebook
averageGrades(student)
DB
getGrades(student)
OOP: 8
FP: 6
DB: 10
Stub of grades
system
Mock
objects that register calls they receive
SecurityCentral
DoorMock
WindowMock
securityOn()
close()
close()
Action to verify
Mockito
EasyMock
JMock
PowerMock
Mocking Libraries
LEARNING MOCKITO
A basic primer
Mockito code samples are given only for learning
purposes and don’t necessarily reflect good
practices. We will mock Java library classes for
learning purposes but it is not recommended to
mock third party classes.
Warning
Creating Mock
@Test
@SuppressWarnings("unchecked")
public void creatingAMockUsingMockMethod() {
List<String> mockedList = Mockito.mock(List.class);
assertThat(mockedList, is(notNullValue()));
}
Using mock method
Creating Mock
// Rule will automatically init mocks which have
annotation
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@Mock private List<String> mockedList;
@Test
public void creatingAMockUsingRule() {
// Mock will already be created for use
assertThat(mockedList, is(notNullValue()));
}
Using rule
Verification
@Test
public void simpleVerification() {
// perform some interactions, mock remembers all
mockedList.add("one");
mockedList.clear();
// Verification of interactions, selectively
Mockito.verify(mockedList).add("one");
Mockito.verify(mockedList).clear();
}
Stubbing
@Test
public void simpleStubbingReturnValueFromMethod() {
// Stubbing or training the mock
Mockito.when(mockedList.get(0)).thenReturn("one");
// Using the stubbed mock
assertThat(mockedList.get(0), is(equalTo("one")));
// Because we have not trained the mock to expect
argument 1
assertThat(mockedList.get(1), is(nullValue()));
}
Returning value from method
Stubbing
@Test
public void stubbingExceptionFromMethod() {
// Train mocked list to throw exception when called
with argument 1
Mockito.when(mockedList.get(1)).thenThrow(
new RuntimeException("Stubbing exception"));
exception.expect(RuntimeException.class);
exception.expectMessage("Stubbing exception");
mockedList.get(1);
}
Throwing exception from method
Stubbing
@Test
public void stubbingExceptionFromVoidMethod() {
Mockito.doThrow(RuntimeException.class)
.when(mockedList).clear();
exception.expect(RuntimeException.class);
mockedList.clear();
}
Throwing exception from void method
Verification
@Test
public void verifyExactNumberOfInvocations() {
mockedList.add("one");
mockedList.add("two"); mockedList.add("two");
mockedList.add("three"); mockedList.add("three");
mockedList.add("three");
// verify exact invocations
Mockito.verify(mockedList).add("one"); // default is 1 time
Mockito.verify(mockedList, Mockito.times(1)).add("one");
Mockito.verify(mockedList, Mockito.times(2)).add("two");
Mockito.verify(mockedList, Mockito.times(3)).add("three");
}
Exact number of invocations
Verification
@Test
public void verifyAtleastOrAtmostInvocations() {
mockedList.add("one");
mockedList.add("two"); mockedList.add("two");
mockedList.add("three"); mockedList.add("three");
mockedList.add("three");
// verify atleast or atmost invocations
Mockito.verify(mockedList, Mockito.atLeastOnce()).add("two");
Mockito.verify(mockedList, Mockito.atLeast(2)).add("three");
Mockito.verify(mockedList, Mockito.atMost(5)).add("three");
}
Atleast or At-most invocations
Verification
@Test
public void verifyNeverInvocations() {
mockedList.add("one");
// verify never invocations
Mockito.verify(mockedList, Mockito.never()).add("two");
Mockito.verify(mockedList, Mockito.never()).add("three");
}
Never invocation
Verification
@Test
public void verifyInorderInvocationWithSingleMock()
{
mockedList.add("added first");
mockedList.add("added second");
InOrder inOrder = Mockito.inOrder(mockedList);
inOrder.verify(mockedList).add("added first");
inOrder.verify(mockedList).add("added second");
}
In order invocations (single mock)
Verification
@Test
public void verifyInorderInvocationWithMultipleMocks() {
firstMock.add("first");
secondMock.add("second");
thirdMock.add("third");
// Not necessary to include all mocks
InOrder inOrder = Mockito.inOrder(firstMock, thirdMock);
inOrder.verify(firstMock).add("first");
inOrder.verify(thirdMock).add("third");
}
In order invocations (multiple mocks)
Mockito Behavior Verification
WeatherObservable observable = new WeatherObservable();
observable.addObserver(mockObserver1);
observable.addObserver(mockObserver2);
// Winter is coming :P
observable.setWeather("WINTER");
verify(mockObserver1).update(observable, "WINTER");
verify(mockObserver2).update(observable, "WINTER");
LEARNING MOCKITO
A basic primer
END OF
Behavior Verification Strategies
MANUAL
VERIFICATION
MOCKING
LIBRARY
Behavior Verification Strategies
compared
1. Complicates test logic
2. Need to repeat it for
all classes
3. Time consuming and
error prone
4. Test readability
suffers
1. No test logic
2. No repetition
3. Only time consumed
is to learn the library
4. Test readability
greatly improved
F.I.R.S.T Principle
FAST
ISOLATED/INDEPENDENT
REPEATABLE
SELF-VALIDATING
THOROUGH
Fast
Developer should not hesitate to run tests
Thousands of tests per minute
Independent/Isolated
No dependency on other test
Order should not matter
Each test should leave system in clean state
Repeatable
No dependency on external environment such
as Randomness, System time
Tests must fail with same reason on multiple
execution
Self-Validating
Each test MUST have an assertion
No manual inspection needed to verify results
Thorough
Test for scenarios not coverage (please!!!)
Test business logic not setters/getters
Exceptional scenarios such as DB
connectivity failure, timeout, invalid input
Dependency Injection
Not all injections have to be painful!
Thermostat Example
Please refer to source code for Thermostat1
As a factory inspector, I want a thermostat, so that I can set
target temperature for my machines and log on console if
temperature fluctuates to too high or low levels
Requirement
Alert
Log alert on
console
Detect temperature
using Chip121
hardware
Naive Example
<<creates>>
<<creates>>
Thermostat
Please refer to source code for Thermostat1
Chip121TemperatureSensor
Log alerts when
temperature
fluctuates away
from target
temperature
Naive Example
Please refer to source code for Thermostat1
public Thermostat1(double targetTemperature) {
this.targetTemperature = targetTemperature;
// Taking decision of type of sensor by itself
this.sensor = new Chip121TemperatureSensor();
this.alert = new Alert();
}
Thermostat Example
Please refer to source code for Thermostat1
As a factory inspector, I want a thermostat, so that I can set
target temperature for my machines using sensor and log
on console if temperature fluctuates to too high or low
levels
Requirement
Thermostat Example
Please refer to source code for Thermostat1
As a thermostat manufacturer, I want the thermostat to be
able to use different sensors because some newer
thermostat models are better at sensing temperature.
Requirement
Naive Example
Please refer to source code for Thermostat1
Currently we can’t reuse because
● Thermostat1 creates sensor from constructor
leaving us helpless if we wan’t to choose other sensor
● There is no interface for sensor
Naive Example
Please refer to source code for Thermostat1
Alert
Log alert on
console
Detect temperature
using Chip121
hardware
<<creates>>
<<creates>>
Thermostat Chip121TemperatureSensor
Directly creates
concrete class
Log alerts when
temperature
fluctuates away
from target
temperature
Extract Interface
Detect temperature
using Chip121
hardware
Chip121TemperatureSensor
Please refer to source code for Thermostat5
Detect temperature
using some hardware
<<I>> TemperatureSensor
Detect temperature
using XYZ hardware
XYZTemperatureSensor
Use interface
Please refer to source code for Thermostat5
Detect temperature
using some hardware
<<creates>>
Thermostat <<I>> TemperatureSensor
Log alerts when
temperature
fluctuates away
from target
temperature
Directly creates
concrete class
Chip121TemperatureSensor XYZTemperatureSensor
Interface is defined
Dependency Injection
Please refer to source code for Thermostat5
Detect temperature
using some hardware
<<depends>>
Thermostat <<I>> TemperatureSensor
Log alerts when
temperature
fluctuates away
from target
temperature
Chip121TemperatureSensor XYZTemperatureSensor
Interface is defined and
dependency is injected
via constructor
Dependency Injection
Please refer to source code for Thermostat5
// Dependency
private final TemperatureSensor sensor;
Thermostat2(double targetTemperature,
TemperatureSensor sensor) {
this.targetTemperature = targetTemperature;
this.sensor = sensor;
...
}
Dependency Injection
When constructing objects, the act of connecting
objects with other objects, or “injecting” objects into
other objects. It is the “D” in S.O.L.I.D principles.
● Constructor Injection (preferred way)
● Field Injection (way frameworks do it using
annotation)
● Method injection (using setters)
Dependency Injection
new Thermostat2(24, new Chip121TemperatureSensor(),
...);
new Thermostat2(24, new XYZTemperatureSensor(),
...);
Injecting the sensor
Thermostat Example
Please refer to source code for Thermostat1
As a thermostat manufacturer, I want the thermostat to be
able to use different sensors because some newer
thermostat models are better at sensing temperature.
Requirement
Naive Example
Please refer to source code for Thermostat1
As a air conditioner manufacturer, I want to control
compressor and heater using thermostat, so that I can
maintain room temperature by switching on/off
heater/compressor
Requirement
Naive Example
Please refer to source code for Thermostat1
Currently we can’t reuse because
● Thermostat2 creates Alert which can only log to
console
● There is no interface for alert mechanism
if (tooCold(temperature)) {
alert.temperatureTooLow(temperature);
}
Naive Example
Please refer to source code for Thermostat1
Here alert is not a dependency. But rather a notification that it
needs to send to some other objects. Just like Observer pattern.
Observer entities will react to that notification and apply custom
business logic like logging to console, controlling compressor or
heater
Please refer to source code for Thermostat1
Extract Interface
Log temperature
fluctuations to console
ConsoleAlert
Callback to receive
temperature
fluctuation events
Switch on/off controller
based on temperature
fluctuation
CompressorController
<<I>> TemperatureListener
...
Please refer to source code for Thermostat1
Use Interface
<<creates>>
Thermostat <<I>> TemperatureListener
Log alerts when
temperature
fluctuates away
from target
temperature
Directly creates
concrete class
ConsoleAlert CompressorController
Interface is defined
Callback to receive
temperature
fluctuation events
Please refer to source code for Thermostat1
Open for extension
<<notifies>>
Thermostat <<I>> TemperatureListener
Notifies when
temperature
fluctuates away
from target
temperature
ConsoleAlert CompressorController
Interface is defined &
multiple receivers
Callback to receive
temperature
fluctuation events
*
1
...
Refactored Naive
Example
Please refer to source code for Thermostat5
<<depends>>
Thermostat
<<I>>
TemperatureSensor
Notifies when
temperature
fluctuates away
from target
temperature
Chip121... XYZ...
<<I>>
TemperatureListener
Console... Compressor...
<<notifies>>
1
*
...
...
Seam
Refactored Naive
Example
Please refer to source code for Thermostat1
Thermostat3 thermostat5 = new Thermostat3(24,
temperatureSensor);
thermostat5.addListener(new HeaterController());
thermostat5.addListener(new CompressorController());
Thermostat Example
Please refer to source code for Thermostat1
As a air conditioner manufacturer, I want to control
compressor and heater using thermostat, so that I can
maintain room temperature by switching on/off
heater/compressor
Requirement
TESTS
are first client of your code
Assume previous requirements were given by Unit tests
Naive Example
Please refer to source code for Thermostat1
As a unit test
1) I want to use mock sensor as actual chipset cannot be
used in unit testing
2) use mock listener as heater or compressor will not be
available in unit testing and mocking sysout is not a
good option
Requirement
Testable Naive Example
<<depends>>
Thermostat
<<I>>
TemperatureSensor
<<I>>
TemperatureListener
<<notifies>> 1
*
Seam
Mocks (controlled by tests)
Refactored Naive
Example
Please refer to source code for Thermostat1
@Test
public void generatesTooHotAlertIfTempratureExceedsTargetTemperatureThreshold()
{
when(mockSensor.getReading()).thenReturn(24.11);
thermostat.addListener(mockListener);
...
verify(mockListener).onTemperatureTooHigh(24.11);
}
Observations
● Unit testing makes code loosely coupled
● If we started with the goal of unit testing then our code naturally
becomes reusable
● Isolating System Under Test requires Seam
● Improves design of your units (not always)
Thermostat Example
Please refer to source code for Thermostat1
As a unit test
1) I want to use mock sensor as actual chipset cannot be
used in unit testing
2) use mock listener as heater or compressor will not be
available in unit testing and mocking sysout is not a
good option
Requirement
Dealing with Randomness
Math.random() or new Random() etc
Naive Example
Please refer to source code for Thermostat1
As a lucky game player, I want a game of dice so that I can
play the game to test my luck and win rewards.
We will roll dice thrice and if dice has face value 6 all th
then user will get reward.
Requirement
Randomness Example
Please refer to source code for DiceOfFortuneGame
public class DiceGame {
private Random random = new Random();
/**
* Rolls dice three times and returns result. Game can be won if dice value
is equal to 6.
*/
public boolean rollDice() {
return isLucky() && isLucky()
&& isLucky();
}
private boolean isLucky() {
return roll() == 6;
}
}
private int roll() {
return random.ints(1, 7)
.findFirst().getAsInt();
}
Source of
unpredictable random
value
<<creates>>
DiceGame java.util.Random
Roll dice and
return result of
the game
Randomness Example
Please refer to source code for DiceOfFortuneGame
Randomness Example
Please refer to source code for DiceOfFortuneGame
public class DiceGameTest {
private DiceGame game = new DiceGame();
// Coverage yes but no good as no assertion
@Test
public void testNoException() {
boolean unpredictableResult = game.rollDice();
// We cannot predict whether game is working or not
because it is using random value and we cannot control it.
System.out.println(unpredictableResult);
}
}
java.unit.Random
Source of
unpredictable
random value
Refactored Randomness Example
<<depends>>
Please refer to source code for DiceOfFortuneGame
Roll dice and return
result of the game
DiceGame
Seam
Constructor
Injection
Refactored Randomness Example
public class DiceGame {
private Random random;
DiceGame(Random random) {
this.random = random;
}
public boolean rollDice() {
return isLucky() && isLucky() && isLucky();
}
private boolean isLucky() {
return roll() == 6;
}
}
Please refer to source code for DiceOfFortuneGame
Refactored Randomness Example
Please refer to source code for DiceOfFortuneGame
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Random mockRandom;
@InjectMocks
private DiceGame game;
@Test
public void gameIsWonIfThreeConsecutiveRollsOfDiceFaceShows6() {
Mockito.when(mockRandom.ints(Mockito.anyInt(), Mockito.anyInt())
.findFirst()).thenReturn(of(6));
boolean result = game.rollDice();
assertTrue(result);
verify(mockRandom.ints(1, 7),
times(3)).findFirst();
}
Randomness Example
Please refer to source code for Thermostat1
As a lucky game player, I want a game of dice so that I can
play the game to test my luck and win rewards.
We will roll dice once and if dice face has value greater
than 3 then user will get reward.
Requirement
Detect temperature
using Chip121
hardware
java.util.Random
Source of
unpredictable
random value
Randomness Example
<<creates>>
Please refer to source code for Thermostat1
Chip121TemperatureSensor
Randomness Example
Please refer to source code for Thermostat1
class Chip121TemperatureSensor {
// Creating random makes tests unpredictable
private Random random = new Random();
private double getReading() {
return random.doubles(targetTemperature - 1.0,
targetTemperature + 1.0).findFirst().getAsDouble();
}
}
Randomness Example
Please refer to source code for DiceOfFortuneGame
Constructor Injection of Random
If we mock Random, we violate
○ Don’t mock classes you don’t own
■ Ugly to mock Connection, PreparedStatement
○ “Mock interfaces only” ( unless testing legacy code)
Please refer to source code for Thermostat1
Extract Interface
Provide Random value
using java library
SystemRandomProvider
Source of
unpredictable random
value
Provide fixed value on
each call
(for testing only)
FixedRandomProvider
<<I>> RandomProvider
...
Random
Provider
Source of
unpredictable
random value
Refactored Randomness Example
<<depends>>
Please refer to source code for Thermostat1
Detect temperature
using Chip121
hardware
Chip121Temperature Sensor
FixedRandom
Provider
SystemRandom
Provider
Seam
Refactored Randomness Example
Please refer to source code for Thermostat1
class Chip121TemperatureSensor {
private RandomProvider randomProvider;
private double getReading() {
return randomProvider.nextDouble(targetTemperature - 1.0,
targetTemperature + 1.0);
}
}
Refactored Randomness Example
Please refer to source code for Thermostat1
@Test
public void
sensorGeneratesTemperatureWithinPlusOrMinusOneRangeOfTargetTemperature() {
sensor.getReading();
// Tests are incomplete without this assertion. Why?
verify(fixedRandomProvider, times(1)).nextDouble(TARGET_TEMPERATURE - 1.0,
TARGET_TEMPERATURE + 1.0);
}
@Test
public void returnsTheCurrentTemperatureReading() {
fixedRandomProvider.setRandom(24);
assertThat(sensor.getReading(), is(equalTo(24.0)));
}
Dealing with
Asynchronous code
Thread or ExecutorService or ScheduledExecutorService
Read Sensor data
Every Seconds
Thread Example
<<creates>>
Thread.sleep(1000)
Thermostat
Please refer to source code for Thermostat1
Thread
Log alerts when
temperature
fluctuates away
from target
temperature
<<call>>
public Thermostat3(double targetTemperature) {
...
// Creates thread which makes it difficult to test
new Thread(() -> {
while (!Thread.interrupted()) {
sleep();
setCurrentTemperatureReading(sensor.getReading());
}
}).start();
}
Thread Example
Please refer to source code for Thermostat1
@Test
public void test() throws InterruptedException {
Thermostat3 thermostat1 = new Thermostat3(24);
Thread.sleep(2000); // Wait till first reading is taken
double temperature = thermostat1.getCurrentTemperature();
...
}
Thread Example
Please refer to source code for Thermostat1
Caution: Slow Tests!!
Thread Example
Please refer to source code for Thermostat1
Currently Thermostat is hard to test
● Thermostat creates thread in constructor
● Test case is hard to debug as sensor reading performed
in separate Thread
How make Thermostat testable??
Please refer to source code for Thermostat1
Thread Example
Run Task in separate
Thread using Java
Executor API
DefaultTaskScheduler
Run the task at regular
interval
Provide control over
scheduled task by
capturing the task
FakeTaskScheduler
<<I>> TaskScheduler
...
Task Scheduler
Run the task at
regular interval
Refactored Thread Example
<<depends>>
Please refer to source code for Thermostat1
Schedule the task
which read sensor
data every 1 sec
Thermostat
DefaultTaskSched
uler
FakeTaskSched
uler
Seam
Refactored Thread Example
Please refer to source code for Thermostat1
Thermostat4(double targetTemperature, RandomProvider
randomProvider, TaskScheduler taskScheduler) {
this.taskScheduler = taskScheduler;
// Does not manage thread itself but depends on task
scheduler to do the job
taskScheduler.scheduleFixedDelayTask(() ->
setCurrentTemperatureReading(sensor.getReading()),
1, TimeUnit.SECONDS);
}
Refactored Thread Example
Please refer to source code for Thermostat1
@Test
public void generatesTooHotAlertIfTempratureExceedsTargetTemperatureThreshold(){
when(mockSensor.getReading()).thenReturn(24.11);
// Run the scheduled task in sync (current thread only)
fakeTaskScheduler.tick();
verify(mockListener).onTemperatureTooHigh(24.11);
}
Dealing with Global State
@Test
public void test1() {
new DreadfulController().doSomething();
}
@Test
public void test2() {
new DreadfulController().doSomething();
}
Will these tests pass?
@Test
public void thisTestMayFail() {
new DreadfulController().doSomething();
}
@Test
public void thisTestMayFailToo() {
new DreadfulController().doSomething();
}
Any test can fail
randomly
Why?
@Test
public void thisTestMayFail() {
new DreadfulController().doSomething();
}
@Test
public void thisTestMayFailToo() {
new DreadfulController().doSomething();
}
Any test can fail
randomly
Hidden static
static
variable
Hidden static
DreadfulController
calls
DreadfulDependency
DreadfulHelper
calls
static
static
variable
uses
Indirectly
uses
DreadfulController
DreadfulController DreadfulDependency
DreadfulDependency
Hidden Global State
public class DreadfulHelper {
private static int globalEvilState = 0;
public static void doDreadfulStuff() {
if (++globalEvilState % 2 == 0) {
throw new RuntimeException("Global evil
hidden state will haunt you forever!");
}
}
}
Global State is Evil!
(static variables)
Solution: Global State
You control the state not the class
DreadfulController
calls
PleasantHelper
calls
instance
variable
uses
Use same instance of
dependency in
production and
different in test
DreadfulController
PleasantController PleasantDependency
Solution: Global State
Test code
@Before
public void createController() {
//Fresh clean copy of PleasantHelper for each test
controller = new PleasantController(new PleasantDependency(
new PleasantHelper()));
}
@Test
public void thisTestCannotFailAsItIsIsolated() {
controller.doSomething();
}
@Test
public void thisTestCannotFailAsItIsIsolatedToo() {
controller.doSomething();
}
Solution: Global State
Production code
PleasantHelper sharedHelper = new PleasantHelper();
PleasantDependency pleasantDependency
= new PleasantDependency(sharedHelper);
PleasantController controller1
= new PleasantController(pleasantDependency);
PleasantController controller2
= new PleasantController(pleasantDependency);
controller1.doSomething();
// will throw exception because of shared PleasantHelper
controller2.doSomething();
Solution: Global State
Controlled shared state
● Don’t enforce global state by using static variables
● Use shared state to mimic global state
● Responsibility of maintaining shared state is on the
objects that create and weave objects together like
Factories or Mother classes
● Unpredictable test results (flaky)
○ Reduces confidence in tests
● Test order affects test results
○ Some test may change state which affects other test
● Issues in running tests in parallel
○ Different test threads affect same state
Global State Issues
Singleton Pattern
a curious case of global state
Singleton example
BadUserController
Processes user
request and
alerts if
processing
takes time
SNMPService ConfigurationManager
snmp-servic
e.xml
snmp-servic
e.xml
snmp-servic
e.xml
Socket
<<depends>>
static call to
getInstance()
Singleton Singleton
<<opens>> <<reads>>
External world
static call to
getInstance()
Singleton example
BadUserController
Processes user
request and
alerts if
processing
takes time
SNMPService ConfigurationManager
snmp-servic
e.xml
snmp-servic
e.xml
snmp-servic
e.xml
Socket
<<depends>>
static call to
getInstance()
Singleton Singleton
<<opens>> <<reads>>
External world
static call to
getInstance()
Unintentional baggage
Unintentional
baggage
Singleton example
An ugly attempt to test
UserRequest userRequest = newUserRequest();
User user = new User();
user.setFirstName(TEST_USER_NAME);
user.setId(userRequest.getUserId());
...
ConfigurationManager.getInstance().init();
// Tries to establish connection to SNMP service
SNMPService.getInstance().init();
controller.processUserRequest(userRequest);
// Can't really test this now because its tightly coupled
with SNMP
Singleton = Global State
● Uses a static variable hence global state (isolation issue)
● Can only be accessed via static method (Seam issue)
● Not all singletons are evil, we need singletons but not as
frequently as we use
● Don’t use Singleton only to increase scope of object, so that
anywhere in the code we can use it
Makes code difficult to test
Singleton example
if (TimeUnit.MILLISECONDS.toSeconds(end - start) > 1) {
SNMPService.getInstance().generateAlert("User lookup
operation took more than 1 sec");
}
● Creating SNMP connection is not feasible in unit test
● Static call means procedural code
● No Seam available to test
How to solve this?
Singleton example
Inject Singleton
BadUserController
Processes user
request and
alerts if
processing
takes time
SNMPService ConfigurationManager
snmp-servic
e.xml
snmp-servic
e.xml
snmp-servic
e.xml
Socket
<<depends>>
Singleton Singleton
<<opens>> <<reads>>
External world
static call to
getInstance()
Real
implementation
MockSNMPServic
e
Constructor
Injection
Constructor
Injection
Test code
Refactored Singleton example
<<depends>>
<<I>> AlertService
SNMPService MockAlertService
Interface is defined &
dependency injection
Service that can
generate alert
1
...
GoodUserController
Processes user
request and
alerts if
processing
takes time
1
Test code uses
Refactored Singleton example
Hows tests should look like
controller = new GoodUserController(.., mockAlertService, ..);
… Some more test training code here
// Exercise SUT
controller.processUserRequest(userRequest);
verify(mockAlertService).generateAlert("User lookup operation
took more than 1 sec");
Isolating
System.nanoTime()
or
System.currentTimeInMillis()
Or
LocalDate (Java8)
a special case of global state
Time dependency example
long start = System.nanoTime();
User user = userRepository.getUser(request.getUserId());
long end = System.nanoTime();
if (TimeUnit.MILLISECONDS.toSeconds(end - start) > 1) {
// Generate alert
}
How to test alert scenario?
Time dependency example
Mockito.doAnswer(invocationOnMock -> {
// Introduce real delay, make tests slower! Yayy
Thread.sleep(2000);
return user;
}).when(mockHibernateRepository)
.getUser(Mockito.anyString());
A naive attempt - Thread.sleep()
Introducing real delay with Thread.sleep() makes tests slower
Time dependency example
Problems with naive attempt
Few tests with
Thread.sleep()
Ain’t that bad!
Add a few more to
the mix
Tests take 4-5 mins
Becomes a habit!
Well it’s all right.
Tests take 20 mins
Few sprints later
Tests take hours!
Why is build so
slow?
Dev: “Tests take too
long. Can’t check it
everytime I change
something”
Somebody: “Build is
too slow, let’s skip
unit tests. Anyway
they don’t add
value”
Doomsday strikes
Peppering threading logic in tests
● Makes them harder to read and understand
○ Like production code wasn’t enough
● Makes them flaky/unpredictable
○ Cause yeah getting threading right is so easy!
Right?
Time dependency example
Problems with naive attempt
SystemTicker MockTicker
Abstracting System.nanoTime()
<<I>> Ticker
Source for fetching
elapsed time
+ nanoTime()
SystemUnderTest
<<depends>>
long start =
ticker.nanoTime();
User user =
userRepository.getUser(request
.getUserId());
long end = ticker.nanoTime(); return System::nanoTime; // returns what you want
SystemTimeSource MockTimeSource
Abstracting System.currentTime...
<<I>> TimeSource
Source for fetching
clock time
+ currentTimeInMillis()
SystemUnderTest
<<depends>>
long time =
timeSource.currentTimeInMillis();
return System::currentTimeInMillis; // returns what you want
LocalDate dependency example
public DiwaliPromotionFactory1(
DiwaliDayProvider diwaliDayProvider) {
this.diwaliDayProvider = diwaliDayProvider;
}
public DiwaliPromotion get() {
LocalDate now = LocalDate.now();
// Calculate which day will be Diwali this year
LocalDate diwali = diwaliDayProvider.get(now.getYear());
return new DiwaliPromotion(diwali.atStartOfDay());
}
How to test?
LocalDate dependency example
@Test
public void locatesCurrentYearDiwaliDay() {
DiwaliPromotionFactory1 factory =
new DiwaliPromotionFactory1(mockDiwaliDayProvider);
factory.get();
// Well works all well in 2018, but starts failing in 2019
verify(mockDiwaliDayProvider).get(2018);
}
Bad Attempt - Ticking time bomb
LocalDate dependency example
public DiwaliPromotionFactory2(DiwaliDayProvider diwaliDayProvider,
Clock clock) {
this.diwaliDayProvider = diwaliDayProvider;
this.clock = clock;
}
@Test
public void locatesCurrentYearDiwaliDay() {
Clock fixedClock = newFixedClockAt(FIXED_DATE);
DiwaliPromotionFactory2 factory =
new DiwaliPromotionFactory2(mockDiwaliDayProvider, fixedClock);
factory.get();
// Because date is fixed, tests will always pass
verify(mockDiwaliDayProvider).get(FIXED_DATE.getYear());
}
Inject Clock instance
Object Creation Is A
Responsibility
How To Think About new Operator
Credit Card Processing Example
AuthenticatorPage
R
ChargePage
R
Authenticator
S
CreditCardProcessor
S
UserRepository
S
OfflineQueue
S
Database
S
S - Singleton scope
R - Request scope
Constructor
Collaborator
Same construction and collaboration graph
Credit: Miško Hevery
Credit Card Processing Example
Same construction and collaboration graph
● Difficult to test as each object is creating the objects it
needs (it’s ok to create value objects or internal
helpers)
● new operator is scattered all around the codebase
● It’s not the responsibility of business objects to create
objects via new or via getInstance (indirectly new)
Credit Card Processing Example
S - Singleton scope
R - Request scope
Constructor
Collaborator
With Dependency Injection
AuthenticatorPage
R
ChargePage
R
Authenticator
S
CreditCardProcessor
S
UserRepository
S
OfflineQueue
S
Database
S
ApplicationFactory
(Singleton lifetime)
S
RequestFactory
(HTTP Request Lifetime)
S
Credit Card Processing Example
With Dependency Injection
● The construction (instantiation) of objects is moved to
Factory classes which is where majority of new
operator is located
● Object construction is separated from collaboration
graph
● ApplicationFactory creates all singleton scope
objects
● RequestFactory creates all request scope objects and
depends on singleton scope objects
Credit Card Processing Example
With Dependency Injection
// All objects created by application factory are singleton in scope.
public class ApplicationFactory {
private RequestFactory requestFactory;
public void build() {
// leaf of graph has no dependency, so gets created first
Database database = new Database();
OfflineQueue offlineQueue = new OfflineQueue(database);
Authenticator authenticator = new Authenticator(database);
UserRepository userRepository = new UserRepository(database);
CreditCardProcessor creditCardProcessor = new CreditCardProcessor(offlineQueue);
requestFactory = new RequestFactory(authenticator, userRepository,
creditCardProcessor);
}
public RequestFactory getRequestFactory() {
return requestFactory;
}
}
Credit Card Processing Example
With Dependency Injection
// All objects created by request factory are HTTP request scope.
public class RequestFactory {
private final Authenticator authenticator;
private final UserRepository userRepository;
private final CreditCardProcessor creditCardProcessor;
public RequestFactory(Authenticator authenticator, UserRepository userRepository,
CreditCardProcessor creditCardProcessor) {
this.authenticator = authenticator;
this.userRepository = userRepository;
this.creditCardProcessor = creditCardProcessor;
}
public AuthenticationPage build() {
return new AuthenticationPage(authenticator,
new ChargePage(creditCardProcessor, userRepository));
}
}
Show of hands
How many of you feel with time it gets
difficult to add new features to existing code?
Show of hands
How many of you want to change structure of
code to make it easier to add new feature but
can’t do because lack of understanding of
code or lack of confidence?
Refactoring
Make small changes to code without affecting
the overall behavior of the visible behavior of
software
“First make the change easy,
then make the easy change”
- Kent Beck on Refactoring
Refactoring
Automated Testing
RELIES ON
LEARNING MOCKITO
Few advanced concepts
Stubbing
@Test
public void stubbingReturnValueForAnyArgument() {
// For any index value return "fixed value"
// List will behave as if all indexes have value "fixed
value"
Mockito.when(mockedList.get(Mockito.anyInt()))
.thenReturn("fixed value");
assertThat(mockedList.get(999), is(equalTo("fixed value")));
}
Matching any argument - Argument Matchers
Verification
@Test
public void verifyingArgumentUsingArgumentMatchers() {
mockedList.get(987);
// Will test if get() was called with any index argument
Mockito.verify(mockedList).get(Mockito.anyInt());
}
Verifying any argument - Argument Matchers
Argument Matchers
Mockito.anyInt();
Mockito.anyDouble();
Mockito.anyString();
Mockito.anyCollection();
Mockito.anySet();
Mockito.anyObject();
Mockito.any(CustomClass.class);
// Many more
Many built-in and we can create custom
Spies
public class Chip121TemperatureSensorTest {
private static final double TARGET_TEMPERATURE = 24;
@Spy
private FixedRandomProvider fixedRandomProvider = new FixedRandomProvider();
private TemperatureSensor sensor;
@Test
public void sensorGeneratesTemperatureWithinPlusOrMinusOneRangeOfTargetTemperature() {
sensor.getReading();
// Tests are incomplete without this assertion. Why?
verify(fixedRandomProvider, times(1)).nextDouble(TARGET_TEMPERATURE - 1.0,
TARGET_TEMPERATURE + 1.0);
}
Real objects + Mocking
Unit Testing
Best practices
Don’t Verify Your Own Stubbing
Mockito.when(mockedObject.method(1))
.thenReturn("one");
// This only proves that mocking library works
assertThat(mockedObject.method(1),
is(equalTo("one")));
We don’t want to test mocking library
Only verify indirect behavior
Verify how SUT interacted with your mock
WeatherObservable observable = new WeatherObservable();
observable.addObserver(mockObserver1);
observable.setWeather("WINTER");
// Verifying whether mock was indirectly called
verify(mockObserver1).update(observable, "WINTER");
Testing Internals
public class Customer {
private CustomerStatus status = CustomerStatus.REGULAR;
public void promote() {
this.status = CustomerStatus.PREFERRED;
}
public double getDisount() {
return this.status == CustomerStatus.PREFERRED ? 0.5 : 0;
}
}
@Test
public void promotingChangesCustomerStatusToPreferred() {
Customer customer = new Customer();
customer.promote();
// Exposing internal state makes tests brittle, what if tomorrow
// we have to refactor and remove enum to use boolean
assertThat(customer.status, is(equalTo(Customer.CustomerStatus.PREFERRED)));
}
Don’t expose internal state just for testing purposes
Testing via Public API
@Test
public void aPromotedCustomerEnjoysDiscountedRate() {
Customer customer = new Customer();
customer.promote();
// Checking for side effect using public API and not
checking
// internal state. So Customer can be refactored to use
boolean
// instead of enum easily without breaking unit tests
assertThat(customer.getDisount(), is(equalTo(0.5)));
}
Allows for easier refactoring
Hidden Dependencies
public class ClassHidingDependency {
public void doSomething() {
try {
// Looking up objects from thin air
Context.getX().getY().getZmanager().init();
Context.getX().getY().getZmanager().doSomething();
} catch (Exception e) {
// No problem somebody else might have called init.
// Eating exception - Yikes!!
}
}
}
Don’t hide dependencies
Explicit Dependencies
public class ClassExplicitDependency {
private final ZManager zManager;
public ClassExplicitDependency(ZManager zManager) {
// zManager instance being passed must be already initialized
this.zManager = zManager;
}
public void doSomething() {
// Directly using ZManager, as it expects an already initialized
object
// to be passed via constructor
zManager.doSomething();
}
}
Perform Dependency Injection
Global State Cleanup - reset
public class ResettableSingleton {
public static final ResettableSingleton INSTANCE = new
ResettableSingleton();
private int doneCount;
private ResettableSingleton() {
}
// Will work when single tests are run, but will fail randomly when
tests are run in parallel
public void reset() {
doneCount = 0;
}
}
Doesn’t really work when tests run in parallel
Singleton → singleton
// Container can ensure only one instance of Lenient
is created. Hence ensuring singleton property
public class Lenient {
private int doneCount;
public Lenient() {
// Not enforcing singletoness, tests can create independent
instances which are isolated from each other
}
}
Don’t enforce singletoness via private constructor
Avoid Threads in Unit Tests
@Test
public void test() throws InterruptedException {
sut.doSomethingInvolvingThreads();
// Don't do this
Thread.sleep(1000);
if (getResult() == null) {
// if result not yet calculated sleep some more
Thread.sleep(1000);
}
Object result = getResult();
// assertion
}
Makes code slower and hard to read and troubleshoot
Inject Non-Threaded ExecutorService
private SystemUnderTest sut =
new SystemUnderTest(MoreExecutors.directExecutor());
@Test
public void test() {
sut.doSomethingInvolvingThreads();
// Result is already available because background work was
performed by current thread
Object result = getResult();
}
Use Guava DirectExecutor or FakeExecutor
Ending Notes
Tests for “legacy code” do
look ugly
But first it’s important to test and then improve the
legacy code and tests
Develop Custom Test APIs
It’s important that tests are free of repetition, so make
utilities and APIs that others can reuse
?
Thank you! Any Questions?

Contenu connexe

Tendances

Grails Spock Testing
Grails Spock TestingGrails Spock Testing
Grails Spock Testing
TO THE NEW | Technology
 

Tendances (20)

Unit testing with java
Unit testing with javaUnit testing with java
Unit testing with java
 
Testing And Mxunit In ColdFusion
Testing And Mxunit In ColdFusionTesting And Mxunit In ColdFusion
Testing And Mxunit In ColdFusion
 
Grails Spock Testing
Grails Spock TestingGrails Spock Testing
Grails Spock Testing
 
EasyMock for Java
EasyMock for JavaEasyMock for Java
EasyMock for Java
 
Junit 4.0
Junit 4.0Junit 4.0
Junit 4.0
 
Best practices unit testing
Best practices unit testing Best practices unit testing
Best practices unit testing
 
Unit test-using-spock in Grails
Unit test-using-spock in GrailsUnit test-using-spock in Grails
Unit test-using-spock in Grails
 
Embrace Unit Testing
Embrace Unit TestingEmbrace Unit Testing
Embrace Unit Testing
 
Writing good unit test
Writing good unit testWriting good unit test
Writing good unit test
 
3 j unit
3 j unit3 j unit
3 j unit
 
JUnit Pioneer
JUnit PioneerJUnit Pioneer
JUnit Pioneer
 
Junit
JunitJunit
Junit
 
JUnit & Mockito, first steps
JUnit & Mockito, first stepsJUnit & Mockito, first steps
JUnit & Mockito, first steps
 
Test driven development - JUnit basics and best practices
Test driven development - JUnit basics and best practicesTest driven development - JUnit basics and best practices
Test driven development - JUnit basics and best practices
 
Rhino Mocks
Rhino MocksRhino Mocks
Rhino Mocks
 
JUNit Presentation
JUNit PresentationJUNit Presentation
JUNit Presentation
 
Junit and testNG
Junit and testNGJunit and testNG
Junit and testNG
 
05 junit
05 junit05 junit
05 junit
 
JUnit- A Unit Testing Framework
JUnit- A Unit Testing FrameworkJUnit- A Unit Testing Framework
JUnit- A Unit Testing Framework
 
Unit testing in xcode 8 with swift
Unit testing in xcode 8 with swiftUnit testing in xcode 8 with swift
Unit testing in xcode 8 with swift
 

Similaire à Effective Unit Testing

Qtp 92 Tutorial Anil
Qtp 92 Tutorial AnilQtp 92 Tutorial Anil
Qtp 92 Tutorial Anil
guest3373d3
 
Qtp 9.2 Tutorial
Qtp 9.2 TutorialQtp 9.2 Tutorial
Qtp 9.2 Tutorial
guest37ae7f
 
qtp 9.2 features
qtp 9.2 featuresqtp 9.2 features
qtp 9.2 features
krishna3032
 
Unit testing php-unit - phing - selenium_v2
Unit testing   php-unit - phing - selenium_v2Unit testing   php-unit - phing - selenium_v2
Unit testing php-unit - phing - selenium_v2
Tricode (part of Dept)
 

Similaire à Effective Unit Testing (20)

Java Unit Test - JUnit
Java Unit Test - JUnitJava Unit Test - JUnit
Java Unit Test - JUnit
 
Presentation
PresentationPresentation
Presentation
 
Test Driven Development with PHPUnit
Test Driven Development with PHPUnitTest Driven Development with PHPUnit
Test Driven Development with PHPUnit
 
Intro To Unit and integration Testing
Intro To Unit and integration TestingIntro To Unit and integration Testing
Intro To Unit and integration Testing
 
We Are All Testers Now: The Testing Pyramid and Front-End Development
We Are All Testers Now: The Testing Pyramid and Front-End DevelopmentWe Are All Testers Now: The Testing Pyramid and Front-End Development
We Are All Testers Now: The Testing Pyramid and Front-End Development
 
Qtp 92 Tutorial Anil
Qtp 92 Tutorial AnilQtp 92 Tutorial Anil
Qtp 92 Tutorial Anil
 
Qtp 9.2 Tutorial
Qtp 9.2 TutorialQtp 9.2 Tutorial
Qtp 9.2 Tutorial
 
Qtp 92 Tutorial769
Qtp 92 Tutorial769Qtp 92 Tutorial769
Qtp 92 Tutorial769
 
Qtp 92 Tutorial769
Qtp 92 Tutorial769Qtp 92 Tutorial769
Qtp 92 Tutorial769
 
qtp 9.2 features
qtp 9.2 featuresqtp 9.2 features
qtp 9.2 features
 
Qtp 92 Tutorial
Qtp 92 TutorialQtp 92 Tutorial
Qtp 92 Tutorial
 
Ppt Qtp
Ppt QtpPpt Qtp
Ppt Qtp
 
Qtp 92 Tutorial769
Qtp 92 Tutorial769Qtp 92 Tutorial769
Qtp 92 Tutorial769
 
Unit testing php-unit - phing - selenium_v2
Unit testing   php-unit - phing - selenium_v2Unit testing   php-unit - phing - selenium_v2
Unit testing php-unit - phing - selenium_v2
 
Testing
TestingTesting
Testing
 
Grails unit testing
Grails unit testingGrails unit testing
Grails unit testing
 
TEST AUTOMATION: AKA QUALITY CONTROL
TEST AUTOMATION: AKA QUALITY CONTROLTEST AUTOMATION: AKA QUALITY CONTROL
TEST AUTOMATION: AKA QUALITY CONTROL
 
TEST AUTOMATION: AKA QUALITY CONTROL
TEST AUTOMATION: AKA QUALITY CONTROLTEST AUTOMATION: AKA QUALITY CONTROL
TEST AUTOMATION: AKA QUALITY CONTROL
 
Testing And Drupal
Testing And DrupalTesting And Drupal
Testing And Drupal
 
Unit Testing
Unit TestingUnit Testing
Unit Testing
 

Dernier

JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
Max Lee
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
mbmh111980
 

Dernier (20)

A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfA Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
 
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
 
10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf
 
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
 
Top Mobile App Development Companies 2024
Top Mobile App Development Companies 2024Top Mobile App Development Companies 2024
Top Mobile App Development Companies 2024
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
 
iGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by SkilrockiGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by Skilrock
 
JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
 
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product UpdatesGraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
 
Agnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in KrakówAgnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in Kraków
 
A Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data MigrationA Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data Migration
 
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
 
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with StrimziStrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
 
Benefits of Employee Monitoring Software
Benefits of  Employee Monitoring SoftwareBenefits of  Employee Monitoring Software
Benefits of Employee Monitoring Software
 
Crafting the Perfect Measurement Sheet with PLM Integration
Crafting the Perfect Measurement Sheet with PLM IntegrationCrafting the Perfect Measurement Sheet with PLM Integration
Crafting the Perfect Measurement Sheet with PLM Integration
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
 
Designing for Privacy in Amazon Web Services
Designing for Privacy in Amazon Web ServicesDesigning for Privacy in Amazon Web Services
Designing for Privacy in Amazon Web Services
 
Workforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdfWorkforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdf
 
CompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdfCompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdf
 

Effective Unit Testing

  • 1. Effective Unit Testing Objects, Dependencies and Mocks
  • 2. @npathai on GitHub Narendra Pathai Developing high performance systems, Software Craftsman, Trainer & Mentor
  • 3. Unit Test = Test per class We can choose as small or as large a unit /
  • 4. Coverage = Good Tests If there is no assertion, coverage is of no use!!! /
  • 5. Unit Testing = Slow Development We fail constantly and are extremely slow when learning anything, does it mean we are still slow?! /
  • 6. No place for excuses Think of learning to walk, talk, typing!
  • 7. Must see test case failing once If a test case hasn’t failed even once, how do you know it is EVEN checking something?
  • 9. Web of Objects ● Communication patterns are set of rules ● Rules govern roles they play, what messages they can send and when ● Communication patterns gives meaning to possible relationships among objects
  • 10. Web of Objects ● Either the object returns value to the caller ● Either the object changes it’s state ● Either it interacts with other object via message passing ● Makes an external call that changes state of that system such as database call, web service call etc.
  • 11. Calculator Display add(int, int) subtract(int, int) show(int) clear() currentValue() calls SYSTEM UNDER FOCUS COLLABORATOR INTERACTIONS calls int int MESSAGES CLIENT
  • 12. Web of Objects ● set.add(val) ○ Adds value to the set (change of state) ○ Returns boolean (message passing) ● list.clear() ○ Clears all elements (change of state) ○ Doesn’t return any value (no message passing) ○ Need to call additional methods to verify behavior eg. size(), isEmpty()
  • 13. Web of Objects ● observable.notifyObservers() ○ Neither returns any value (no message passing) ○ Nor does it change any state ○ It interacts with other objects/observers and passes message to those objects
  • 14. Testing Units in Isolation TEST TEST ? ? ? ? Mocks Seam TEST Real Unit under test Internal Structure
  • 18. State Verification A B C SETUP ACT VERIFY TEARDOWN Exercise System Under Test (SUT) sut.method(..) Get State sut.getState() ! ? Indirect Output Real Mock Collaborators Credit: xUnit Test Patterns
  • 19. State Verification ArrayList<Integer> list = new ArrayList<>(); // Exercise the system under test list.add(1); // State verification - verify list is no more empty assertFalse(list.isEmpty());
  • 20. State Verification // Create a new thread Thread t = new Thread(() -> {}); // State verification - Verify it is in NEW state assertThat(t.getState(), is(equalTo(Thread.State.NEW)));
  • 22. Behavior Verification ● How to test if SUT does not have any state? ● How to test if SUT does not change state?
  • 23. Behavior Verification When SUT does not change state or return value or cause any visible side effect, it MUST interact with other object or component. We can test that interaction!
  • 25. ● Places where we can alter system flow ● Essentially polymorphism (ideally interfaces) ● Allow us to isolate SUT when unit testing ● Procedural code (static) has no seams ● Difficult to test procedural code SEAM Not all procedural code is bad! Eg. static utilities like Math.abs(), Math.max() etc.
  • 26. Behavior Verification WeatherObservable observable = new WeatherObservable(); WeatherObserver observer = new WeatherObserver(); observable.addObserver(observer); // exercise the SUT observable.setWeather("FALL"); // Verify that observer was notified // How to verify that?
  • 29. Manual Behavior Verification // A test specific observer that tracks if method was called class TestObserver implements Observer { private boolean called = false; @Override public void update(Observable o, Object arg) { this.called = true; } }
  • 30. Manual Behavior Verification WeatherObservable observable = new WeatherObservable(); // Custom hand rolled observer TestObserver observer = new TestObserver(); observable.addObserver(observer); // exercise the SUT observable.setWeather("FALL"); // Manual behavior verification assertTrue(observer.called);
  • 31. Manual Behavior Verification How to test the following: ● Observer should only be called once ● Observer should be called with exact argument
  • 32. Manual Behavior Verification // A test specific observer that tracks if method was called class TestObserver implements Observer { private int count = 0; private Object arg; @Override public void update(Observable o, Object arg) { this.count++; this.arg = arg; } }
  • 33. Manual Behavior Verification // Manual behavior verification // Test Readability suffering assertEquals(1, observer.count); assertEquals("FALL", observer.arg);
  • 34. Manual Behavior Verification How to test the following: ● Any one of multiple methods should be called ● Different methods called with different arguments ● Observers should be called in exact order in which they were added ● Many more advanced verifications..
  • 35. Manual Behavior Verification We are already starting to add logic in test code itself!! So there is a possibility that test code has bugs. Me: So now should we write test cases for test code?! Your reaction: That ain’t happening!
  • 37. Behavior Verification Strategies 1. Complicates test logic 2. Need to repeat it for all classes 3. Time consuming and error prone 4. Test readability suffers MOCKING LIBRARY
  • 40. Stubbing ● Providing hard-coded answers/behaviors from methods called during test ● Test case wise answer/behavior can be changed ● Stubbing logic MUST be simple For instance: ● Returning a test/dummy user from DAO layer ● Throwing exception from DAO layer to replicate error scenario a.k.a Training phase
  • 41. Exp. Verification ● Verify how the SUT should have interacted with its collaborators ● Verify the communication contract between SUT and its collaborators ● MUST be present in every test case For instance: ● Verify Observer#update() method was called once only ● Verify argument was matching ● Verify Observers were called in order they were added a.k.a assertion phase
  • 42. Mocks Stubs Stubbing + Verification Hard-coded results No verification Fakes Working but simplified version of production code, like DAO using collection rather than DB Dummy Objects passed around but never actually used
  • 43. Fake a working simplified version LoginService AccountDAO FakeAccountDAO HashMap Fake implementation login(user) getPasswordHash() DB
  • 44. Stub object trained with predefined data GradesService Gradebook averageGrades(student) DB getGrades(student) OOP: 8 FP: 6 DB: 10 Stub of grades system
  • 45. Mock objects that register calls they receive SecurityCentral DoorMock WindowMock securityOn() close() close() Action to verify
  • 48. Mockito code samples are given only for learning purposes and don’t necessarily reflect good practices. We will mock Java library classes for learning purposes but it is not recommended to mock third party classes. Warning
  • 49. Creating Mock @Test @SuppressWarnings("unchecked") public void creatingAMockUsingMockMethod() { List<String> mockedList = Mockito.mock(List.class); assertThat(mockedList, is(notNullValue())); } Using mock method
  • 50. Creating Mock // Rule will automatically init mocks which have annotation @Rule public MockitoRule mockito = MockitoJUnit.rule(); @Mock private List<String> mockedList; @Test public void creatingAMockUsingRule() { // Mock will already be created for use assertThat(mockedList, is(notNullValue())); } Using rule
  • 51. Verification @Test public void simpleVerification() { // perform some interactions, mock remembers all mockedList.add("one"); mockedList.clear(); // Verification of interactions, selectively Mockito.verify(mockedList).add("one"); Mockito.verify(mockedList).clear(); }
  • 52. Stubbing @Test public void simpleStubbingReturnValueFromMethod() { // Stubbing or training the mock Mockito.when(mockedList.get(0)).thenReturn("one"); // Using the stubbed mock assertThat(mockedList.get(0), is(equalTo("one"))); // Because we have not trained the mock to expect argument 1 assertThat(mockedList.get(1), is(nullValue())); } Returning value from method
  • 53. Stubbing @Test public void stubbingExceptionFromMethod() { // Train mocked list to throw exception when called with argument 1 Mockito.when(mockedList.get(1)).thenThrow( new RuntimeException("Stubbing exception")); exception.expect(RuntimeException.class); exception.expectMessage("Stubbing exception"); mockedList.get(1); } Throwing exception from method
  • 54. Stubbing @Test public void stubbingExceptionFromVoidMethod() { Mockito.doThrow(RuntimeException.class) .when(mockedList).clear(); exception.expect(RuntimeException.class); mockedList.clear(); } Throwing exception from void method
  • 55. Verification @Test public void verifyExactNumberOfInvocations() { mockedList.add("one"); mockedList.add("two"); mockedList.add("two"); mockedList.add("three"); mockedList.add("three"); mockedList.add("three"); // verify exact invocations Mockito.verify(mockedList).add("one"); // default is 1 time Mockito.verify(mockedList, Mockito.times(1)).add("one"); Mockito.verify(mockedList, Mockito.times(2)).add("two"); Mockito.verify(mockedList, Mockito.times(3)).add("three"); } Exact number of invocations
  • 56. Verification @Test public void verifyAtleastOrAtmostInvocations() { mockedList.add("one"); mockedList.add("two"); mockedList.add("two"); mockedList.add("three"); mockedList.add("three"); mockedList.add("three"); // verify atleast or atmost invocations Mockito.verify(mockedList, Mockito.atLeastOnce()).add("two"); Mockito.verify(mockedList, Mockito.atLeast(2)).add("three"); Mockito.verify(mockedList, Mockito.atMost(5)).add("three"); } Atleast or At-most invocations
  • 57. Verification @Test public void verifyNeverInvocations() { mockedList.add("one"); // verify never invocations Mockito.verify(mockedList, Mockito.never()).add("two"); Mockito.verify(mockedList, Mockito.never()).add("three"); } Never invocation
  • 58. Verification @Test public void verifyInorderInvocationWithSingleMock() { mockedList.add("added first"); mockedList.add("added second"); InOrder inOrder = Mockito.inOrder(mockedList); inOrder.verify(mockedList).add("added first"); inOrder.verify(mockedList).add("added second"); } In order invocations (single mock)
  • 59. Verification @Test public void verifyInorderInvocationWithMultipleMocks() { firstMock.add("first"); secondMock.add("second"); thirdMock.add("third"); // Not necessary to include all mocks InOrder inOrder = Mockito.inOrder(firstMock, thirdMock); inOrder.verify(firstMock).add("first"); inOrder.verify(thirdMock).add("third"); } In order invocations (multiple mocks)
  • 60. Mockito Behavior Verification WeatherObservable observable = new WeatherObservable(); observable.addObserver(mockObserver1); observable.addObserver(mockObserver2); // Winter is coming :P observable.setWeather("WINTER"); verify(mockObserver1).update(observable, "WINTER"); verify(mockObserver2).update(observable, "WINTER");
  • 61. LEARNING MOCKITO A basic primer END OF
  • 63. Behavior Verification Strategies compared 1. Complicates test logic 2. Need to repeat it for all classes 3. Time consuming and error prone 4. Test readability suffers 1. No test logic 2. No repetition 3. Only time consumed is to learn the library 4. Test readability greatly improved
  • 65. Fast Developer should not hesitate to run tests Thousands of tests per minute
  • 66. Independent/Isolated No dependency on other test Order should not matter Each test should leave system in clean state
  • 67. Repeatable No dependency on external environment such as Randomness, System time Tests must fail with same reason on multiple execution
  • 68. Self-Validating Each test MUST have an assertion No manual inspection needed to verify results
  • 69. Thorough Test for scenarios not coverage (please!!!) Test business logic not setters/getters Exceptional scenarios such as DB connectivity failure, timeout, invalid input
  • 70. Dependency Injection Not all injections have to be painful!
  • 71. Thermostat Example Please refer to source code for Thermostat1 As a factory inspector, I want a thermostat, so that I can set target temperature for my machines and log on console if temperature fluctuates to too high or low levels Requirement
  • 72. Alert Log alert on console Detect temperature using Chip121 hardware Naive Example <<creates>> <<creates>> Thermostat Please refer to source code for Thermostat1 Chip121TemperatureSensor Log alerts when temperature fluctuates away from target temperature
  • 73. Naive Example Please refer to source code for Thermostat1 public Thermostat1(double targetTemperature) { this.targetTemperature = targetTemperature; // Taking decision of type of sensor by itself this.sensor = new Chip121TemperatureSensor(); this.alert = new Alert(); }
  • 74. Thermostat Example Please refer to source code for Thermostat1 As a factory inspector, I want a thermostat, so that I can set target temperature for my machines using sensor and log on console if temperature fluctuates to too high or low levels Requirement
  • 75. Thermostat Example Please refer to source code for Thermostat1 As a thermostat manufacturer, I want the thermostat to be able to use different sensors because some newer thermostat models are better at sensing temperature. Requirement
  • 76. Naive Example Please refer to source code for Thermostat1 Currently we can’t reuse because ● Thermostat1 creates sensor from constructor leaving us helpless if we wan’t to choose other sensor ● There is no interface for sensor
  • 77. Naive Example Please refer to source code for Thermostat1 Alert Log alert on console Detect temperature using Chip121 hardware <<creates>> <<creates>> Thermostat Chip121TemperatureSensor Directly creates concrete class Log alerts when temperature fluctuates away from target temperature
  • 78. Extract Interface Detect temperature using Chip121 hardware Chip121TemperatureSensor Please refer to source code for Thermostat5 Detect temperature using some hardware <<I>> TemperatureSensor Detect temperature using XYZ hardware XYZTemperatureSensor
  • 79. Use interface Please refer to source code for Thermostat5 Detect temperature using some hardware <<creates>> Thermostat <<I>> TemperatureSensor Log alerts when temperature fluctuates away from target temperature Directly creates concrete class Chip121TemperatureSensor XYZTemperatureSensor Interface is defined
  • 80. Dependency Injection Please refer to source code for Thermostat5 Detect temperature using some hardware <<depends>> Thermostat <<I>> TemperatureSensor Log alerts when temperature fluctuates away from target temperature Chip121TemperatureSensor XYZTemperatureSensor Interface is defined and dependency is injected via constructor
  • 81. Dependency Injection Please refer to source code for Thermostat5 // Dependency private final TemperatureSensor sensor; Thermostat2(double targetTemperature, TemperatureSensor sensor) { this.targetTemperature = targetTemperature; this.sensor = sensor; ... }
  • 82. Dependency Injection When constructing objects, the act of connecting objects with other objects, or “injecting” objects into other objects. It is the “D” in S.O.L.I.D principles. ● Constructor Injection (preferred way) ● Field Injection (way frameworks do it using annotation) ● Method injection (using setters)
  • 83. Dependency Injection new Thermostat2(24, new Chip121TemperatureSensor(), ...); new Thermostat2(24, new XYZTemperatureSensor(), ...); Injecting the sensor
  • 84. Thermostat Example Please refer to source code for Thermostat1 As a thermostat manufacturer, I want the thermostat to be able to use different sensors because some newer thermostat models are better at sensing temperature. Requirement
  • 85. Naive Example Please refer to source code for Thermostat1 As a air conditioner manufacturer, I want to control compressor and heater using thermostat, so that I can maintain room temperature by switching on/off heater/compressor Requirement
  • 86. Naive Example Please refer to source code for Thermostat1 Currently we can’t reuse because ● Thermostat2 creates Alert which can only log to console ● There is no interface for alert mechanism
  • 87. if (tooCold(temperature)) { alert.temperatureTooLow(temperature); } Naive Example Please refer to source code for Thermostat1 Here alert is not a dependency. But rather a notification that it needs to send to some other objects. Just like Observer pattern. Observer entities will react to that notification and apply custom business logic like logging to console, controlling compressor or heater
  • 88. Please refer to source code for Thermostat1 Extract Interface Log temperature fluctuations to console ConsoleAlert Callback to receive temperature fluctuation events Switch on/off controller based on temperature fluctuation CompressorController <<I>> TemperatureListener ...
  • 89. Please refer to source code for Thermostat1 Use Interface <<creates>> Thermostat <<I>> TemperatureListener Log alerts when temperature fluctuates away from target temperature Directly creates concrete class ConsoleAlert CompressorController Interface is defined Callback to receive temperature fluctuation events
  • 90. Please refer to source code for Thermostat1 Open for extension <<notifies>> Thermostat <<I>> TemperatureListener Notifies when temperature fluctuates away from target temperature ConsoleAlert CompressorController Interface is defined & multiple receivers Callback to receive temperature fluctuation events * 1 ...
  • 91. Refactored Naive Example Please refer to source code for Thermostat5 <<depends>> Thermostat <<I>> TemperatureSensor Notifies when temperature fluctuates away from target temperature Chip121... XYZ... <<I>> TemperatureListener Console... Compressor... <<notifies>> 1 * ... ... Seam
  • 92. Refactored Naive Example Please refer to source code for Thermostat1 Thermostat3 thermostat5 = new Thermostat3(24, temperatureSensor); thermostat5.addListener(new HeaterController()); thermostat5.addListener(new CompressorController());
  • 93. Thermostat Example Please refer to source code for Thermostat1 As a air conditioner manufacturer, I want to control compressor and heater using thermostat, so that I can maintain room temperature by switching on/off heater/compressor Requirement
  • 94. TESTS are first client of your code Assume previous requirements were given by Unit tests
  • 95. Naive Example Please refer to source code for Thermostat1 As a unit test 1) I want to use mock sensor as actual chipset cannot be used in unit testing 2) use mock listener as heater or compressor will not be available in unit testing and mocking sysout is not a good option Requirement
  • 97. Refactored Naive Example Please refer to source code for Thermostat1 @Test public void generatesTooHotAlertIfTempratureExceedsTargetTemperatureThreshold() { when(mockSensor.getReading()).thenReturn(24.11); thermostat.addListener(mockListener); ... verify(mockListener).onTemperatureTooHigh(24.11); }
  • 98. Observations ● Unit testing makes code loosely coupled ● If we started with the goal of unit testing then our code naturally becomes reusable ● Isolating System Under Test requires Seam ● Improves design of your units (not always)
  • 99. Thermostat Example Please refer to source code for Thermostat1 As a unit test 1) I want to use mock sensor as actual chipset cannot be used in unit testing 2) use mock listener as heater or compressor will not be available in unit testing and mocking sysout is not a good option Requirement
  • 101. Naive Example Please refer to source code for Thermostat1 As a lucky game player, I want a game of dice so that I can play the game to test my luck and win rewards. We will roll dice thrice and if dice has face value 6 all th then user will get reward. Requirement
  • 102. Randomness Example Please refer to source code for DiceOfFortuneGame public class DiceGame { private Random random = new Random(); /** * Rolls dice three times and returns result. Game can be won if dice value is equal to 6. */ public boolean rollDice() { return isLucky() && isLucky() && isLucky(); } private boolean isLucky() { return roll() == 6; } } private int roll() { return random.ints(1, 7) .findFirst().getAsInt(); }
  • 103. Source of unpredictable random value <<creates>> DiceGame java.util.Random Roll dice and return result of the game Randomness Example Please refer to source code for DiceOfFortuneGame
  • 104. Randomness Example Please refer to source code for DiceOfFortuneGame public class DiceGameTest { private DiceGame game = new DiceGame(); // Coverage yes but no good as no assertion @Test public void testNoException() { boolean unpredictableResult = game.rollDice(); // We cannot predict whether game is working or not because it is using random value and we cannot control it. System.out.println(unpredictableResult); } }
  • 105. java.unit.Random Source of unpredictable random value Refactored Randomness Example <<depends>> Please refer to source code for DiceOfFortuneGame Roll dice and return result of the game DiceGame Seam Constructor Injection
  • 106. Refactored Randomness Example public class DiceGame { private Random random; DiceGame(Random random) { this.random = random; } public boolean rollDice() { return isLucky() && isLucky() && isLucky(); } private boolean isLucky() { return roll() == 6; } } Please refer to source code for DiceOfFortuneGame
  • 107. Refactored Randomness Example Please refer to source code for DiceOfFortuneGame @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Random mockRandom; @InjectMocks private DiceGame game; @Test public void gameIsWonIfThreeConsecutiveRollsOfDiceFaceShows6() { Mockito.when(mockRandom.ints(Mockito.anyInt(), Mockito.anyInt()) .findFirst()).thenReturn(of(6)); boolean result = game.rollDice(); assertTrue(result); verify(mockRandom.ints(1, 7), times(3)).findFirst(); }
  • 108. Randomness Example Please refer to source code for Thermostat1 As a lucky game player, I want a game of dice so that I can play the game to test my luck and win rewards. We will roll dice once and if dice face has value greater than 3 then user will get reward. Requirement
  • 109. Detect temperature using Chip121 hardware java.util.Random Source of unpredictable random value Randomness Example <<creates>> Please refer to source code for Thermostat1 Chip121TemperatureSensor
  • 110. Randomness Example Please refer to source code for Thermostat1 class Chip121TemperatureSensor { // Creating random makes tests unpredictable private Random random = new Random(); private double getReading() { return random.doubles(targetTemperature - 1.0, targetTemperature + 1.0).findFirst().getAsDouble(); } }
  • 111. Randomness Example Please refer to source code for DiceOfFortuneGame Constructor Injection of Random If we mock Random, we violate ○ Don’t mock classes you don’t own ■ Ugly to mock Connection, PreparedStatement ○ “Mock interfaces only” ( unless testing legacy code)
  • 112. Please refer to source code for Thermostat1 Extract Interface Provide Random value using java library SystemRandomProvider Source of unpredictable random value Provide fixed value on each call (for testing only) FixedRandomProvider <<I>> RandomProvider ...
  • 113. Random Provider Source of unpredictable random value Refactored Randomness Example <<depends>> Please refer to source code for Thermostat1 Detect temperature using Chip121 hardware Chip121Temperature Sensor FixedRandom Provider SystemRandom Provider Seam
  • 114. Refactored Randomness Example Please refer to source code for Thermostat1 class Chip121TemperatureSensor { private RandomProvider randomProvider; private double getReading() { return randomProvider.nextDouble(targetTemperature - 1.0, targetTemperature + 1.0); } }
  • 115. Refactored Randomness Example Please refer to source code for Thermostat1 @Test public void sensorGeneratesTemperatureWithinPlusOrMinusOneRangeOfTargetTemperature() { sensor.getReading(); // Tests are incomplete without this assertion. Why? verify(fixedRandomProvider, times(1)).nextDouble(TARGET_TEMPERATURE - 1.0, TARGET_TEMPERATURE + 1.0); } @Test public void returnsTheCurrentTemperatureReading() { fixedRandomProvider.setRandom(24); assertThat(sensor.getReading(), is(equalTo(24.0))); }
  • 116. Dealing with Asynchronous code Thread or ExecutorService or ScheduledExecutorService
  • 117. Read Sensor data Every Seconds Thread Example <<creates>> Thread.sleep(1000) Thermostat Please refer to source code for Thermostat1 Thread Log alerts when temperature fluctuates away from target temperature <<call>>
  • 118. public Thermostat3(double targetTemperature) { ... // Creates thread which makes it difficult to test new Thread(() -> { while (!Thread.interrupted()) { sleep(); setCurrentTemperatureReading(sensor.getReading()); } }).start(); } Thread Example Please refer to source code for Thermostat1
  • 119. @Test public void test() throws InterruptedException { Thermostat3 thermostat1 = new Thermostat3(24); Thread.sleep(2000); // Wait till first reading is taken double temperature = thermostat1.getCurrentTemperature(); ... } Thread Example Please refer to source code for Thermostat1 Caution: Slow Tests!!
  • 120. Thread Example Please refer to source code for Thermostat1 Currently Thermostat is hard to test ● Thermostat creates thread in constructor ● Test case is hard to debug as sensor reading performed in separate Thread How make Thermostat testable??
  • 121. Please refer to source code for Thermostat1 Thread Example Run Task in separate Thread using Java Executor API DefaultTaskScheduler Run the task at regular interval Provide control over scheduled task by capturing the task FakeTaskScheduler <<I>> TaskScheduler ...
  • 122. Task Scheduler Run the task at regular interval Refactored Thread Example <<depends>> Please refer to source code for Thermostat1 Schedule the task which read sensor data every 1 sec Thermostat DefaultTaskSched uler FakeTaskSched uler Seam
  • 123. Refactored Thread Example Please refer to source code for Thermostat1 Thermostat4(double targetTemperature, RandomProvider randomProvider, TaskScheduler taskScheduler) { this.taskScheduler = taskScheduler; // Does not manage thread itself but depends on task scheduler to do the job taskScheduler.scheduleFixedDelayTask(() -> setCurrentTemperatureReading(sensor.getReading()), 1, TimeUnit.SECONDS); }
  • 124. Refactored Thread Example Please refer to source code for Thermostat1 @Test public void generatesTooHotAlertIfTempratureExceedsTargetTemperatureThreshold(){ when(mockSensor.getReading()).thenReturn(24.11); // Run the scheduled task in sync (current thread only) fakeTaskScheduler.tick(); verify(mockListener).onTemperatureTooHigh(24.11); }
  • 126. @Test public void test1() { new DreadfulController().doSomething(); } @Test public void test2() { new DreadfulController().doSomething(); } Will these tests pass?
  • 127. @Test public void thisTestMayFail() { new DreadfulController().doSomething(); } @Test public void thisTestMayFailToo() { new DreadfulController().doSomething(); } Any test can fail randomly Why?
  • 128. @Test public void thisTestMayFail() { new DreadfulController().doSomething(); } @Test public void thisTestMayFailToo() { new DreadfulController().doSomething(); } Any test can fail randomly Hidden static static variable
  • 130. Hidden Global State public class DreadfulHelper { private static int globalEvilState = 0; public static void doDreadfulStuff() { if (++globalEvilState % 2 == 0) { throw new RuntimeException("Global evil hidden state will haunt you forever!"); } } }
  • 131. Global State is Evil! (static variables)
  • 132. Solution: Global State You control the state not the class DreadfulController calls PleasantHelper calls instance variable uses Use same instance of dependency in production and different in test DreadfulController PleasantController PleasantDependency
  • 133. Solution: Global State Test code @Before public void createController() { //Fresh clean copy of PleasantHelper for each test controller = new PleasantController(new PleasantDependency( new PleasantHelper())); } @Test public void thisTestCannotFailAsItIsIsolated() { controller.doSomething(); } @Test public void thisTestCannotFailAsItIsIsolatedToo() { controller.doSomething(); }
  • 134. Solution: Global State Production code PleasantHelper sharedHelper = new PleasantHelper(); PleasantDependency pleasantDependency = new PleasantDependency(sharedHelper); PleasantController controller1 = new PleasantController(pleasantDependency); PleasantController controller2 = new PleasantController(pleasantDependency); controller1.doSomething(); // will throw exception because of shared PleasantHelper controller2.doSomething();
  • 135. Solution: Global State Controlled shared state ● Don’t enforce global state by using static variables ● Use shared state to mimic global state ● Responsibility of maintaining shared state is on the objects that create and weave objects together like Factories or Mother classes
  • 136. ● Unpredictable test results (flaky) ○ Reduces confidence in tests ● Test order affects test results ○ Some test may change state which affects other test ● Issues in running tests in parallel ○ Different test threads affect same state Global State Issues
  • 137. Singleton Pattern a curious case of global state
  • 138. Singleton example BadUserController Processes user request and alerts if processing takes time SNMPService ConfigurationManager snmp-servic e.xml snmp-servic e.xml snmp-servic e.xml Socket <<depends>> static call to getInstance() Singleton Singleton <<opens>> <<reads>> External world static call to getInstance()
  • 139. Singleton example BadUserController Processes user request and alerts if processing takes time SNMPService ConfigurationManager snmp-servic e.xml snmp-servic e.xml snmp-servic e.xml Socket <<depends>> static call to getInstance() Singleton Singleton <<opens>> <<reads>> External world static call to getInstance() Unintentional baggage Unintentional baggage
  • 140. Singleton example An ugly attempt to test UserRequest userRequest = newUserRequest(); User user = new User(); user.setFirstName(TEST_USER_NAME); user.setId(userRequest.getUserId()); ... ConfigurationManager.getInstance().init(); // Tries to establish connection to SNMP service SNMPService.getInstance().init(); controller.processUserRequest(userRequest); // Can't really test this now because its tightly coupled with SNMP
  • 141. Singleton = Global State ● Uses a static variable hence global state (isolation issue) ● Can only be accessed via static method (Seam issue) ● Not all singletons are evil, we need singletons but not as frequently as we use ● Don’t use Singleton only to increase scope of object, so that anywhere in the code we can use it Makes code difficult to test
  • 142. Singleton example if (TimeUnit.MILLISECONDS.toSeconds(end - start) > 1) { SNMPService.getInstance().generateAlert("User lookup operation took more than 1 sec"); } ● Creating SNMP connection is not feasible in unit test ● Static call means procedural code ● No Seam available to test How to solve this?
  • 143. Singleton example Inject Singleton BadUserController Processes user request and alerts if processing takes time SNMPService ConfigurationManager snmp-servic e.xml snmp-servic e.xml snmp-servic e.xml Socket <<depends>> Singleton Singleton <<opens>> <<reads>> External world static call to getInstance() Real implementation MockSNMPServic e Constructor Injection Constructor Injection Test code
  • 144. Refactored Singleton example <<depends>> <<I>> AlertService SNMPService MockAlertService Interface is defined & dependency injection Service that can generate alert 1 ... GoodUserController Processes user request and alerts if processing takes time 1 Test code uses
  • 145. Refactored Singleton example Hows tests should look like controller = new GoodUserController(.., mockAlertService, ..); … Some more test training code here // Exercise SUT controller.processUserRequest(userRequest); verify(mockAlertService).generateAlert("User lookup operation took more than 1 sec");
  • 147. Time dependency example long start = System.nanoTime(); User user = userRepository.getUser(request.getUserId()); long end = System.nanoTime(); if (TimeUnit.MILLISECONDS.toSeconds(end - start) > 1) { // Generate alert } How to test alert scenario?
  • 148. Time dependency example Mockito.doAnswer(invocationOnMock -> { // Introduce real delay, make tests slower! Yayy Thread.sleep(2000); return user; }).when(mockHibernateRepository) .getUser(Mockito.anyString()); A naive attempt - Thread.sleep()
  • 149. Introducing real delay with Thread.sleep() makes tests slower Time dependency example Problems with naive attempt Few tests with Thread.sleep() Ain’t that bad! Add a few more to the mix Tests take 4-5 mins Becomes a habit! Well it’s all right. Tests take 20 mins Few sprints later Tests take hours! Why is build so slow? Dev: “Tests take too long. Can’t check it everytime I change something” Somebody: “Build is too slow, let’s skip unit tests. Anyway they don’t add value” Doomsday strikes
  • 150. Peppering threading logic in tests ● Makes them harder to read and understand ○ Like production code wasn’t enough ● Makes them flaky/unpredictable ○ Cause yeah getting threading right is so easy! Right? Time dependency example Problems with naive attempt
  • 151. SystemTicker MockTicker Abstracting System.nanoTime() <<I>> Ticker Source for fetching elapsed time + nanoTime() SystemUnderTest <<depends>> long start = ticker.nanoTime(); User user = userRepository.getUser(request .getUserId()); long end = ticker.nanoTime(); return System::nanoTime; // returns what you want
  • 152. SystemTimeSource MockTimeSource Abstracting System.currentTime... <<I>> TimeSource Source for fetching clock time + currentTimeInMillis() SystemUnderTest <<depends>> long time = timeSource.currentTimeInMillis(); return System::currentTimeInMillis; // returns what you want
  • 153. LocalDate dependency example public DiwaliPromotionFactory1( DiwaliDayProvider diwaliDayProvider) { this.diwaliDayProvider = diwaliDayProvider; } public DiwaliPromotion get() { LocalDate now = LocalDate.now(); // Calculate which day will be Diwali this year LocalDate diwali = diwaliDayProvider.get(now.getYear()); return new DiwaliPromotion(diwali.atStartOfDay()); } How to test?
  • 154. LocalDate dependency example @Test public void locatesCurrentYearDiwaliDay() { DiwaliPromotionFactory1 factory = new DiwaliPromotionFactory1(mockDiwaliDayProvider); factory.get(); // Well works all well in 2018, but starts failing in 2019 verify(mockDiwaliDayProvider).get(2018); } Bad Attempt - Ticking time bomb
  • 155. LocalDate dependency example public DiwaliPromotionFactory2(DiwaliDayProvider diwaliDayProvider, Clock clock) { this.diwaliDayProvider = diwaliDayProvider; this.clock = clock; } @Test public void locatesCurrentYearDiwaliDay() { Clock fixedClock = newFixedClockAt(FIXED_DATE); DiwaliPromotionFactory2 factory = new DiwaliPromotionFactory2(mockDiwaliDayProvider, fixedClock); factory.get(); // Because date is fixed, tests will always pass verify(mockDiwaliDayProvider).get(FIXED_DATE.getYear()); } Inject Clock instance
  • 156. Object Creation Is A Responsibility How To Think About new Operator
  • 157. Credit Card Processing Example AuthenticatorPage R ChargePage R Authenticator S CreditCardProcessor S UserRepository S OfflineQueue S Database S S - Singleton scope R - Request scope Constructor Collaborator Same construction and collaboration graph Credit: Miško Hevery
  • 158. Credit Card Processing Example Same construction and collaboration graph ● Difficult to test as each object is creating the objects it needs (it’s ok to create value objects or internal helpers) ● new operator is scattered all around the codebase ● It’s not the responsibility of business objects to create objects via new or via getInstance (indirectly new)
  • 159. Credit Card Processing Example S - Singleton scope R - Request scope Constructor Collaborator With Dependency Injection AuthenticatorPage R ChargePage R Authenticator S CreditCardProcessor S UserRepository S OfflineQueue S Database S ApplicationFactory (Singleton lifetime) S RequestFactory (HTTP Request Lifetime) S
  • 160. Credit Card Processing Example With Dependency Injection ● The construction (instantiation) of objects is moved to Factory classes which is where majority of new operator is located ● Object construction is separated from collaboration graph ● ApplicationFactory creates all singleton scope objects ● RequestFactory creates all request scope objects and depends on singleton scope objects
  • 161. Credit Card Processing Example With Dependency Injection // All objects created by application factory are singleton in scope. public class ApplicationFactory { private RequestFactory requestFactory; public void build() { // leaf of graph has no dependency, so gets created first Database database = new Database(); OfflineQueue offlineQueue = new OfflineQueue(database); Authenticator authenticator = new Authenticator(database); UserRepository userRepository = new UserRepository(database); CreditCardProcessor creditCardProcessor = new CreditCardProcessor(offlineQueue); requestFactory = new RequestFactory(authenticator, userRepository, creditCardProcessor); } public RequestFactory getRequestFactory() { return requestFactory; } }
  • 162. Credit Card Processing Example With Dependency Injection // All objects created by request factory are HTTP request scope. public class RequestFactory { private final Authenticator authenticator; private final UserRepository userRepository; private final CreditCardProcessor creditCardProcessor; public RequestFactory(Authenticator authenticator, UserRepository userRepository, CreditCardProcessor creditCardProcessor) { this.authenticator = authenticator; this.userRepository = userRepository; this.creditCardProcessor = creditCardProcessor; } public AuthenticationPage build() { return new AuthenticationPage(authenticator, new ChargePage(creditCardProcessor, userRepository)); } }
  • 163. Show of hands How many of you feel with time it gets difficult to add new features to existing code?
  • 164. Show of hands How many of you want to change structure of code to make it easier to add new feature but can’t do because lack of understanding of code or lack of confidence?
  • 165. Refactoring Make small changes to code without affecting the overall behavior of the visible behavior of software
  • 166. “First make the change easy, then make the easy change” - Kent Beck on Refactoring
  • 169. Stubbing @Test public void stubbingReturnValueForAnyArgument() { // For any index value return "fixed value" // List will behave as if all indexes have value "fixed value" Mockito.when(mockedList.get(Mockito.anyInt())) .thenReturn("fixed value"); assertThat(mockedList.get(999), is(equalTo("fixed value"))); } Matching any argument - Argument Matchers
  • 170. Verification @Test public void verifyingArgumentUsingArgumentMatchers() { mockedList.get(987); // Will test if get() was called with any index argument Mockito.verify(mockedList).get(Mockito.anyInt()); } Verifying any argument - Argument Matchers
  • 172. Spies public class Chip121TemperatureSensorTest { private static final double TARGET_TEMPERATURE = 24; @Spy private FixedRandomProvider fixedRandomProvider = new FixedRandomProvider(); private TemperatureSensor sensor; @Test public void sensorGeneratesTemperatureWithinPlusOrMinusOneRangeOfTargetTemperature() { sensor.getReading(); // Tests are incomplete without this assertion. Why? verify(fixedRandomProvider, times(1)).nextDouble(TARGET_TEMPERATURE - 1.0, TARGET_TEMPERATURE + 1.0); } Real objects + Mocking
  • 174. Don’t Verify Your Own Stubbing Mockito.when(mockedObject.method(1)) .thenReturn("one"); // This only proves that mocking library works assertThat(mockedObject.method(1), is(equalTo("one"))); We don’t want to test mocking library
  • 175. Only verify indirect behavior Verify how SUT interacted with your mock WeatherObservable observable = new WeatherObservable(); observable.addObserver(mockObserver1); observable.setWeather("WINTER"); // Verifying whether mock was indirectly called verify(mockObserver1).update(observable, "WINTER");
  • 176. Testing Internals public class Customer { private CustomerStatus status = CustomerStatus.REGULAR; public void promote() { this.status = CustomerStatus.PREFERRED; } public double getDisount() { return this.status == CustomerStatus.PREFERRED ? 0.5 : 0; } } @Test public void promotingChangesCustomerStatusToPreferred() { Customer customer = new Customer(); customer.promote(); // Exposing internal state makes tests brittle, what if tomorrow // we have to refactor and remove enum to use boolean assertThat(customer.status, is(equalTo(Customer.CustomerStatus.PREFERRED))); } Don’t expose internal state just for testing purposes
  • 177. Testing via Public API @Test public void aPromotedCustomerEnjoysDiscountedRate() { Customer customer = new Customer(); customer.promote(); // Checking for side effect using public API and not checking // internal state. So Customer can be refactored to use boolean // instead of enum easily without breaking unit tests assertThat(customer.getDisount(), is(equalTo(0.5))); } Allows for easier refactoring
  • 178. Hidden Dependencies public class ClassHidingDependency { public void doSomething() { try { // Looking up objects from thin air Context.getX().getY().getZmanager().init(); Context.getX().getY().getZmanager().doSomething(); } catch (Exception e) { // No problem somebody else might have called init. // Eating exception - Yikes!! } } } Don’t hide dependencies
  • 179. Explicit Dependencies public class ClassExplicitDependency { private final ZManager zManager; public ClassExplicitDependency(ZManager zManager) { // zManager instance being passed must be already initialized this.zManager = zManager; } public void doSomething() { // Directly using ZManager, as it expects an already initialized object // to be passed via constructor zManager.doSomething(); } } Perform Dependency Injection
  • 180. Global State Cleanup - reset public class ResettableSingleton { public static final ResettableSingleton INSTANCE = new ResettableSingleton(); private int doneCount; private ResettableSingleton() { } // Will work when single tests are run, but will fail randomly when tests are run in parallel public void reset() { doneCount = 0; } } Doesn’t really work when tests run in parallel
  • 181. Singleton → singleton // Container can ensure only one instance of Lenient is created. Hence ensuring singleton property public class Lenient { private int doneCount; public Lenient() { // Not enforcing singletoness, tests can create independent instances which are isolated from each other } } Don’t enforce singletoness via private constructor
  • 182. Avoid Threads in Unit Tests @Test public void test() throws InterruptedException { sut.doSomethingInvolvingThreads(); // Don't do this Thread.sleep(1000); if (getResult() == null) { // if result not yet calculated sleep some more Thread.sleep(1000); } Object result = getResult(); // assertion } Makes code slower and hard to read and troubleshoot
  • 183. Inject Non-Threaded ExecutorService private SystemUnderTest sut = new SystemUnderTest(MoreExecutors.directExecutor()); @Test public void test() { sut.doSomethingInvolvingThreads(); // Result is already available because background work was performed by current thread Object result = getResult(); } Use Guava DirectExecutor or FakeExecutor
  • 185. Tests for “legacy code” do look ugly But first it’s important to test and then improve the legacy code and tests
  • 186. Develop Custom Test APIs It’s important that tests are free of repetition, so make utilities and APIs that others can reuse
  • 187. ? Thank you! Any Questions?