4. The Real Life Software Challenge
Disadvantages Technical Debt
Traditional form of development
Requirements Code Test
5. Motivation - solving the right problem right
Creating poorly written code
High defect rates
Defects make systems unstable, unpredictable, unusable
Cost of fixing defects caught during testing is typically a
magnitude or two higher than those caught when
introduced into the code base
6. Motivation - solving the right problem right
Lack of maintainability
Code difficult to understand and change
Break functionality elsewhere in the system
Duplication
Failing to meet actual client needs!
7. Test-Driven Development
Learn how to build the thing right - TDD - internal
quality
Learn how to build the right thing - Acceptance TDD -
external quality
12. FIRST
Fast – run (subset of) tests quickly
Isolated – no tests depend on others
Repeatable – run N times, get same result
Self-validating – tests can automatically detect if passed
Timely – written about the same time as code under test
13. How to select the next test
• Going for the highest value vs picking the low-hanging fruit first
• Treading the happy path first vs starting with handling error situations
• Diving first into details vs starting with the big picture
• Exploring uncertain territory vs sticking with the familiar and comfortable
14. Implementation strategies
• Faking it - fake a particular functionality to get back to green
• Triangulation - each of the tests constrains the available solution space to one
dimension, thus they narrow down (”triangulate”) the solution
• Obvious implementation
15. AAA Test Format
@Test
public void sizeOfListReflectsItemsAddedToIt() throws Exception {
List<String> list = new ArrayList<String>(); ARRANGE
list.add("something"); ACT
assertEquals(1, list.size()); ASSERT
}
16. Exercise 1 – Tax Calculator
Given an amount of taxable income, calculate the net
pay according to the following rules:
• First £11,500 are tax-free
• 20% for £11,500 to £45,000
• 40% for £45,000 to £150,000
• 45% for over £150,000
18. Fixtures
A fixture is a fixed state of a set of objects used as a baseline for running tests.
Useful to:
• Remove duplication
• Allow for more focused tests
Used for state-based tests.
19. Test doubles
Are objects that stand in for the real thing. They pretend to be something
they’re not, so that the client has no idea, and they typically do it faster than
the real thing.
Dummy A null or dummy object to be used as parameter value when test
doesn’t care
Stub Class with test-specific implementation (hard-coded responses)
Fake A full implementation for test purposes (e.g. in-memory
database)
Mock An interface with expectations set for the test (implementation
generated at runtime by mocking framework) – interaction-
based test
20. Parameterized creation pattern
Hides the non-important attributes from the setup by extracting the object
creation into a separate creation method, which takes the variable attributes as
arguments and populates the non-important ones with constant or random
values. Our test will populate only the important attributes.
21. Object mother pattern
Is a sophisticated object factory that provides access to a complete object graph
of valid domain objects, including instances in different states.
- Makes it easy for new developers to write tests
- Powerful when integrated with personas
23. Resulting-State Assertion
Exercise some functionality on the fixture objects and afterward assert that the
internal state of the fixture objects match our expectations.
@Test
public void sizeOfListReflectsItemsAddedToIt() throws Exception {
List<String> list = new ArrayList<String>();
list.add("something");
assertEquals(1, list.size()); // state verification
}
24. Guard Assertion
Make explicit the assumptions made about the fixture right before invoking the
functionality we want to test.
@Test
public void listIsNoLongerEmptyAfterAddingAnItemToIt() throws Exception {
List<String> list = new ArrayList<String>();
assertTrue(list.isEmpty()); // guard assertion
list.add("something");
assertFalse(list.isEmpty()); // state verification
}
25. Delta Assertion
Test that the difference(delta) between the initial and after states is what we
expect.
private ArrayList<String> list;
@Test
public void sizeOfListReflectsItemsAddedToIt() throws Exception {
int sizeBefore = list.size(); // record the "before" state
list.add("something");
assertEquals(sizeBefore + 1, list.size()); // delta verification
}
26. Custom Assertion
When the amount of code verifying our expectations vastly exceeds the amount
of code required to invoke the code under test, extract it from the test in order
to encapsulate complex verification logic.
@Test
public void timeslotsAreOnWeekdays() throws Exception {
MeetingCalendar calendar = new MeetingCalendar();
Date time = calendar.nextAvailableStartingTime();
assertIsDuringOfficeHoursOnWeekday(time);}
private void assertIsDuringOfficeHoursOnWeekday(Date time) { … }
28. Exercise 2 – Word Wrapper
Develop a word-wrapping algorithm, which is given a string and
a row-length, it returns a list of word-wrapped-rows.
Examples of behaviour:
• If the row-length is 60, and the input string is 30, the result is
just the input string
• If the row-length is 3, and the input string is "abc def" the
result is "abc", "def”
• If the row-length is 3, and the input string is "abcdef" the
result is "abc", "def”
• If the row-length is 3, and the input string is "abcdef abc" the
result is "abc", "def", "abc”
• With row length 3 and "a b c d e f" the result is "a b", "c d",
"e f"
30. Mutation testing
Principle: change (mutate) certain statements in the source code and check if the
test cases are able to find the errors
- Mutant generation is time-consuming and difficult, so it was automated
- If the tests detect the mutant, then it’s ‘killed’
- Surviving mutants show not tested cases
mutation score = number of mutants killed / total number of mutants
31. Mutation testing types
Value Mutations: values of primary parameters are modified (e.g. change constants)
Decision Mutations: change decisions/conditions to check for design errors (e.g.
arithmetic, logical or relational operators)
Statement Mutations: changes done to the statements by deleting or duplicating the
line (e.g. copy/paste)
34. IBM Team
Products: device drivers
Legacy product
New product using a new platform
Comparison between 7th release of legacy product and 1st
release of new product
The two products exposed the same interfaces for the same set
of devices
35. Microsoft Teams
Products: Windows, MSN, Visual Studio
Windows: Networking team - networking common library written as
a reusable set of modules for different projects
MSN: Web services application
Developer Division: part of the Visual Studio (VS) software
development system
36. Metrics IBM
(legacy+new)
Ms Windows Ms MSN Ms Vista
Defect density not
using TDD
w x y z
Defect density using
TDD
0.6w 0.38x 0.24 y 0.19z
Increase in time to
code
15-20% 25-35% 15% 25-20%
Study Results
37. Exercise 3 – Berlin Clock
- the top yellow lamp blinks to
show seconds - on for even
seconds and off for odd seconds
- the next two rows represent
hours
- the upper row has 5 hour blocks
and is made up of 4 red lamps
- the lower row has 1 hour blocks
and is also made up of 4 red
lamps
- the final two rows represent the
minutes
- the upper row has 5 minute
blocks, and is made up of 11
lamps, every 3rd lamp is red, the
rest are yellow
- the bottom row has 1 minute
blocks, and is made up of 4
yellow lamps
38. TDD Advantages
TDD
Encourages
good design
Prevents over-
engineering
because of flawed
assumptions
Documents how
the code is
intended to work
Gradually
enhances a
working product
Enables effective
learning through early
feedback from client
Less time spent
fixing defects
Produces
testable code
Reduces bugs
with up to 90%