2. Unit Test definition
Unit Test verifies the behavior of small elements in a software
system, which are most often a single class or method. Every UT
must have several characteristics (F.I.R.S.T.):
• Fast
takes a little time to execute (less than 0,01 sec).
• Isolated
does not interact with over parts of a system, failure reasons become obvious.
• Repeatable
run repeatedly in any order, any time.
• Self-Checking
no manual evaluation required.
• Timely
written before the code.
3. Types of tests
Unit Test Component Test System Test Functional Test
Also known as Integration Test Acceptance Test
Depends on
Execution Time ms sec min hour
Description
Check class,
method
Test component
integrity, DB
queries.
System API (WS,
JNDI, etc), external
client interaction
Customer oriented.
Use UI controls,
pages, links, etc.
execution order
Each kind of test has its own area of responsibility, setup efforts, and accordingly,
different execution time.
4. What Unit Test is not?
Test is not a unit if:
• Interacts with over parts of the system (DB, WS, FS, etc.).
• Takes to much time to run (more than 0,01 sec).
• Requires manual setup or verification.
5. Unit Test benefits
Correctly organized and well designed unit tests give
developers several benefits:
• Unit test as documentation.
• Unit test as safety net.
• Defect localization.
• Needless of debugging.
• Design improving.
6. Ineffective Unit Test
When does test become a problem, not a solution?
• Fragile Test – break too often.
• Erratic Test – sometimes it pass and sometimes it fail.
• Manual Intervention – a test requires a person to perform
some manual action each time it is run.
• Obscure Test – it is difficult to understand the test.
• Slow Test – test takes too much time to run.
7. Principles of Test Automation
• Write the Tests First
• Design for Testability
• Use the Front Door First
• Don’t Modify the SUT
• Keep Tests Independent
• Isolate the SUT
• Minimize Test Overlap
• Minimize Untestable Code
• Keep Test Logic Out of Production Code
• Verify One Condition per Test
• Ensure Commensurate Effort and Responsibility
8. How to start testing?
For new code: Test Driven Development (aka Test First).
If you have to add/modify feature on legacy code:
• Identify change points
Find places where you need to make your changes depend sensitively on your
architecture.
• Find test points
Find places where to write tests is easy.
• Break dependencies.
Make sure you can replace dependencies with stub/mock.
• Write test.
Implement test logic which reflects new requirements.
• Refactor.
Remove code duplication and smells.
9. Four-phase test execution
setup
execution
verification
teardown
FixtureSUT
Unit Test
Terminology:
• SUT – software under test.
• Fixture – SUTs dependencies (environment).
Phases:
• Setup: create/find SUT or Fixture.
• Execution: execute something on SUT.
• Verification: Verify state or behavior of SUT/Fixture.
• Teardown: clean up after test.
10. Simple Unit Test example
public class Statistics
{
public static double average(double[] data)
{
if (isEmpty(data)) {
throw new
IllegalArgumentException("Data mustn't be empty.");
}
return calculateAverage(data);
}
private static double calculateAverage(double[] data)
{
double sum = 0;
for (int i = 0; i < data.length; i++) {
sum += data[i];
}
return sum / data.length;
}
private static boolean isEmpty(double[] data)
{
return data == null || data.length == 0;
}
}
import org.testng.Assert;
import org.testng.annotations.Test;
public class StatisticsTest
{
@Test
public void average()
{
final double[] data = {1, 2, 3, 4};
final double expected = 2.5;
final double result = Statistics.average(data);
Assert.assertEquals(expected, result);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void averageForNullData()
{
Statistics.average(null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void averageForEmptyData()
{
Statistics.average(new double[]{});
}
}
Class to test: Test:
11. State verification
State verification almost used to:
• Verify returned value.
• Verify state of the SUT (Front Door).
• Verify state of the Fixture (Back Door).
12. Behavior verification & Test Doubles
Behavior verification used to check SUT correctly calls it’s
fixture. General idea is replace fixture with test doubles (spies
or mock objects) and check them after test execution. Behavior
verification usually used when SUT is stateless.
Test Spy – collects information about method calls. For
example, count of specific method executions.
Mock Object – record some scenario (order and count of
method calls), which must be reproduced on the execution
phase. Checks itself on the verification phase.
13. EasyMock Framework
EasyMock framework help us to create different kinds of test doubles. We
can create double for an interface and class as well.
Simple stubbing example:
import org.easymock.EasyMock;
import org.testng.Assert;
import org.testng.annotations.Test;
interface DataSource
{
String[] getData();
}
public class StubExample
{
@Test
public void createStubByInterface()
{
final String[] dataToReturnByStub = {"value1", "value2"};
final DataSource dataSource = EasyMock.createMock(DataSource.class);
EasyMock.expect(dataSource.getData()).andStubReturn(dataToReturnByStub);
EasyMock.replay(dataSource);
Assert.assertEquals(dataSource.getData(), dataToReturnByStub);
}
}
More information on http://easymock.org/
14. Behavior verification example with EasyMock
public class Application
{
private IReportBuilder reportBuilder;
private IEmployeeStatisticService statisticService;
public Application(IReportBuilder reportBuilder, IEmployeeStatisticService statisticService)
{
this.reportBuilder = reportBuilder;
this.statisticService = statisticService;
}
/**
* The execution flow of this operation is:
* 1) Get statistics report data.
* 2) Render report to a file specified.
* @param fileName
*/
public void buildSalaryReport(Writer writer)
{
final Map reportData = statisticService.createSalaryReportModel();
reportBuilder.renderReport(reportData, writer);
}
}
Class to test:
15. Behavior verification example with EasyMock
import org.easymock.EasyMock;
public class ApplicationTest
{
private IReportBuilder createReportBuilderMock(Writer writerMock)
{
final IReportBuilder reportBuilderMock = EasyMock.createMock(IReportBuilder.class);
reportBuilderMock.renderReport(EasyMock.anyObject(Map.class), EasyMock.eq(writerMock));
EasyMock.expectLastCall();
return reportBuilderMock;
}
private IEmployeeStatisticService createEmployeeStatisticServiceMock()
{
final Map<String, Object> mockReturn = new HashMap<String, Object>();
final IEmployeeStatisticService employeeStatisticServiceMock = EasyMock.createMock(IEmployeeStatisticService.class);
EasyMock.expect(employeeStatisticServiceMock.createSalaryReportModel()).andStubReturn(mockReturn);
return employeeStatisticServiceMock;
}
private Writer createWriterMock()
{
final Writer writerMock = EasyMock.createMock(Writer.class);
return writerMock;
}
. . .
}
Mock setup methods:
16. Behavior verification example with EasyMock
import org.easymock.EasyMock;
import org.testng.annotations.Test;
public class ApplicationTest
{
. . .
@Test
public void applicationFlow()
{
// setup fixture
final Writer writerMock = createWriterMock();
final IEmployeeStatisticService employeeStatisticServiceMock = createEmployeeStatisticServiceMock();
final IReportBuilder reportBuilderMock = createeReportBuilderMock(writerMock);
EasyMock.replay(reportBuilderMock, employeeStatisticServiceMock, writerMock);
// setup CUT.
final Application application =
new Application(reportBuilderMock, employeeStatisticServiceMock);
// execute test.
application.buildSalaryReport(writerMock);
// verify.
EasyMock.verify(reportBuilderMock, employeeStatisticServiceMock, writerMock);
}
}
Test method:
17. Basic Unit Test Antipatterns
The Liar
Excessive Setup
Giant
The Mockery
The Inspector
Generous Leftovers
The Local Hero (Hidden
Dependency)
The Nitpicker
The Secret Catcher
The Dodger
The Loudmouth
The Greedy Catcher
The Sequencer
The Enumerator
The Stranger
The Operating System
Evangelist
Success Against All Odds
The Free Ride
The One
The Peeping Tom
The Slow Poke
18. Links and resources
http://xunitpatterns.com/ - Testing patterns.
http://testng.org - TestNG framework home page.
http://easymock.org/ - EasyMock framework home page.
http://www.agiledata.org/essays/tdd.html - introduction to TDD methodology.
http://blog.james-carr.org/2006/11/03/tdd-anti-patterns/ - TDD anti-patterns.