Post on 11-Jan-2016
transcript
Scrumand
Hyper-productivity
Object Mentor, Inc.
Copyright 1998-2007 by Object Mentor, IncAll Rights Reserved
fitnesse.org
www.objectmentor.com
www.junit.org
Robert C. Martin
2
The inevitable trade-off.
Good (Quality)
Fast (Time to Market)
Cheap (Cost Effectiveness)
Done
Pick any three…
3
Finding the optimum solution.
We need to manage the project to the best possible outcome.
An outcome that maximizes all four qualities.
To do this, we need:
Data.
4
Wouldn’t this be great?
Velocity
0
10
20
30
40
50
60
1/20/20031/27/20032/3/20032/10/20032/17/20032/24/20033/3/20033/10/2003
Story Points
5
…and this…
Story Points Remaining
0
100
200
300
400
500
600
1/20/20031/27/20032/3/20032/10/20032/17/20032/24/20033/3/20033/10/2003
Story Points
6
If we had these two charts on the wall…
Story Points Remaining
0
100
200
300
400
500
600
1/20/20031/27/20032/3/20032/10/20032/17/20032/24/20033/3/20033/10/2003
Story Points
Velocity
0
10
20
30
40
50
60
1/20/20031/27/20032/3/20032/10/20032/17/20032/24/20033/3/20033/10/2003
Story Points
Then managers could just look at them to seeThe status of the project.
7
What is the first thing
known about a project?
The Management Paradox
8
! !
! !
9
The Delivery Date is Frozen
10
The
Spec
V1
The
Spec
V2
The
Spec
V3
The
Spec
V3
The
Spec
VN.1
The
Spec
VN.2
The Spec is Never Frozen
11
Analysis
Design
Implementation
DFDERD
DDST
1 May 1 Nov1 Jul 1 Sep
The Waterfall Model
Managing the Development of Large Software SystemsDr. Winston W. Royce — 1970
12
Royce’s actual diagram.
13
Royce’s Observation
14
Royce’s Conclusion
15
Analysis
Design
Implementation
DFDERD
DDST
1 May 1 Nov1 Jul 1 Sep
Let’s go to a meeting.
16
Iterative Development.
Exploration
Slices cut across all sub-systems
UI
Comms
ControlSome UI, Comms, and
Control for
some behavior
Data is generated and used to calibrate the plan
17
Calculate the Date.
High level analysis and Design
Slices cut across all sub-systems
The Calculated Date
. . . . . . . . [ ]
18
More data shrinks the error bars.
High level analysis and Design
Slices cut across all sub-systems
The Calculated Date
. . . . . . . . [ ]
19
But when we have data…
Story Points Remaining
0
100
200
300
400
500
600
1/20/20031/27/20032/3/20032/10/20032/17/20032/24/20033/3/20033/10/2003
Story Points
Velocity
0
10
20
30
40
50
60
1/20/20031/27/20032/3/20032/10/20032/17/20032/24/20033/3/20033/10/2003
Story Points
Managers can…. manage.
20
The control knobs of project mgt.
Schedule
Quality
Staff Scope
21
SCRUM
Small Releases
WholeTeam
PlanningGame
DailyScrum
22
This can lead to a Mess.
Messy Code is:rigid
fragile
not reusable
has high viscosity
23
Rigidity
The impact of a change cannot be predictedIf not predicted, it cannot be estimatedTime and cost cannot be quantifiedManagers become reluctant to authorize changeOfficial Rigidity for “Roach Motel” modules
Rigidity is the inability to be changed
24
Fragility
A single change requires a cascade of subsequent changesNew errors appear in areas that seem unconnected to the changed areasQuality is unpredictable.The development team loses credibility
Software changes seem to exhibit non-local effects
25
Immobility
Desirable parts of the design are dependent upon undesirable partsThe work and risk of extracting the desirable part may exceed the cost of redeveloping from scratch.
26
Viscosity
When the “right changes” are much more difficult than hacking, the viscosity of the system is high.
Over time, it will become increasingly difficult to continue developing the product.
Viscosity is resistance to fluid motion.
27
What to do?
A good SCRUM team solves this problemBy adopting corrective disciplines.
What are those disciplines?
28
Hyper-productive SCRUM
Metaphor
CollectiveOwnership
SustainablePace
ContinuousIntegration
Small Releases
Acceptance Tests
WholeTeam
PlanningGame
SimpleDesign
Pairing
Test Driven Development
Refactoring
Daily Scrum
29
The Three Laws of TDD
You cannot write any production code until you have written a failing unit test.
You cannot write more of a unit test than is sufficient to fail.
You cannot write more production code than is sufficient to pass the failing test.
30
The Litany of TDD Benefits
30 Second cycle time means less debugging.
A bevy of tests keeps the code flexible
The tests are design documents
Testable code is decoupled code.
User Stories
32
Stories
Name of a feature, or short description
Brief specifications of customer requirementsIs a token for a conversation
Owned by the customer
A good user story criteriaHas business value
Is estimable
Is testable
Return Car
33
Story EstimationTeam assigns a estimate or price to each story
Estimates are in dimensionless but proportional units
A 2 takes twice as much effort as a 1
Customer uses price to juggle priorities and acquire resources
Unknowns can be eliminated through use of spikes, a research story
34
User Stories Define the Project Backlog
Agile Planning
User Stories
Release Planning
Iteration Planning
36
The Purpose of Planning
It is more important to work on the most valuable things first than to predict the future.
• Even the best plans must be continually refined• Requires continuous and meaningful feedback
37
Object Mentor’s Third Law ofAgile Development
“In preparing for battle I have always found that plans are useless, but
planning isindispensable. ”
Dwight David Eisenhower
38
Exploration
AttendeesWhole team
InputProduct VisionInitial Story List (verbal)Critical dates
Output – on Flip Chart SheetsDeck of initial stories.Stakeholder IdentificationCustomer Team IdentificationArchitectural VisionInitial EstimatesVelocity guestimate
39
Release Planning
AttendeesWhole team (as appropriate)
InputsStack of estimated stories
Team Velocity
Critical dates or initial release functionality
OutputsRelease plan – series of iterations, with significant dates
1 2 3 4 5 6 7
40
Release Planning
The 4-way Decision.
ImportantExpensive
ImportantCheap
UnimportantExpensive
UnimportantCheap
41
USER
STORY
USER
STORY
USER
STORY
Managing Scope
USER
STORY
USER
STORY
USER
STORY
USER
STORY
USER
STORY
USER
STORY
USER
STORY
USER
STORY
USER
STORYUSER
STORY
USER
STORY
USER
STORYUSER
STORY
USER
STORY
USER
STORYUSER
STORY
USER
STORY
USER
STORYUSER
STORY
USER
STORY
USER
STORYUSER
STORY
USER
STORY
USER
STORYUSER
STORY
USER
STORY
USER
STORYUSER
STORY
USER
STORY
USER
STORY
GetHigher Business Value
Lower Business Value
Source: Object Mentor Training
Don’t get (yet)
42
Release Planning Summary
Customers write stories
Programmers estimate cost of stories
Customers prioritize based on business value and estimated costs
Release velocity: number of story points completed
Yesterday’s weather
43
Release Plan
1 2 3 4 5 6 7
Release 1 Release 2
44
Iteration Planning
Monday Morning:Confirm the stories for the iteration
Revise estimates based on new information For each story
Write story on top of flip chart pageReview acceptance testsBreak story into engineering tasksDo quick design session as neededSign up.
45
Iteration Planning Summary
Developers break stories into tasks
Developers sign up for stories.
Customers may need to change, split, merge stories to fit iteration
Once in the iteration, the stories are fixed
Iteration velocity: number of story points completed
Never extend the iteration date
46
Iteration Tracking Bulletin Board
Selected AcceptedStarted Done?
47
Iteration Tracking Bulletin Board
Selected AcceptedStarted Done?
48
How do you know a story is done?
1 2 3 4 5 6 7
Working CodePassing Tests
Writing AutomatedAcceptance Tests
Object Mentor, Inc.
Copyright 1998-2001 by Object Mentor, IncAll Rights Reserved
The Role of QA in an Agile Project
51
The Traditional schedule for Quality.
Waiting at the sphincter muscle.
Analysis
Design
Implementation
1 May 1 Nov1 Jul 1 Sep
Test
52
When Quality comes at the end.
It is under the most pressure.
It has the least flexibility.
It is a high stress tedious job.
It is error prone.
Quality cannot be tested in.
The Agile View:
Quality is a specification role…
…not a verification role!
A feature is not specified…
Until it’s acceptance test is written.
55
Who Writes Acceptance Tests?
Business AnalystsHappy path.
QA Test Writers, and TestersCorner and boundary cases.
Try to break it.
56
Acceptance Tests Are…
Automated.
written in a very high level language.
executed frequently.
written by the stakeholders.
57
Imagine you have a button to press that would tell you if the system worked
How often would you press it?
58
It becomes a conditioned response.
When is the best time to write Automated Acceptance Tests?
60
At the start of each iteration.
Exploration
Slices cut across all sub-systems
Test
61
Tests specify each iteration.
Iteration
AcceptanceTests
Defines done-ness.
62
Tests specify each iteration.
Iteration
AcceptanceTests
Defines done-ness.
How do you know a feature is done?
A feature is not done…
Until all it’s acceptance tests pass.
65
Knowing when a feature is done allows...
Story Points Remaining
0
100
200
300
400
500
600
1/20/20031/27/20032/3/20032/10/20032/17/20032/24/20033/3/20033/10/2003
Story Points
Velocity
0
10
20
30
40
50
60
1/20/20031/27/20032/3/20032/10/20032/17/20032/24/20033/3/20033/10/2003
Story Points
These features have passed their AutomatedAcceptance Tests.
What do they look like?
A Simple Example
The result.
69
When is the best time to write AATs?
Astute test managers Talk to the stakeholders days before each iteration.
Write initial acceptance tests for the features they plan to schedule.
Elaborate those acceptance tests once scheduled.
70
When is the best time to run them?
Continuously.
At every check-in.
No change is allowed to break any passing tests.
Continuous Integration.
71
Who runs them?
Developers.
Testers.
Managers.
Stakeholders.
…They are run automatically.
…Results are displayed on the wall.
72
This keeps the data accurate…
Story Points Remaining
0
100
200
300
400
500
600
1/20/20031/27/20032/3/20032/10/20032/17/20032/24/20033/3/20033/10/2003
Story Points
Velocity
0
10
20
30
40
50
60
1/20/20031/27/20032/3/20032/10/20032/17/20032/24/20033/3/20033/10/2003
Story Points
…and actionable.
73
Evolution: Manual Tests
Used for lack of any better approachAre better than nothingTerribly InefficientIn a crunch they are not runUnfortunately still among the most commonly used approach
74
Evolution: Ad Hoc Text-based
CSV or Tab delimited filesAllowed for automation of testsDoes not scale wellVery unconventionalEveryone used a different schemeNot very customer friendly
75
Evolution: XML-based
Still automatedScalableVisions of frameworks beganXML introduces extra complexityNot customer friendly
76
GUI Testers
Mercury
Robot
Canoo
WebRunnerEtc.
77
Testing through GUI is a trap.
We have a client with 15,000 acceptance tests run through the GUI.
Old DOS based GUI.
Can’t update GUI because tests would fail.
Stuck!
FitNesse
The solution we all agreed upon.
79
Writing Requirements as Tests
Requirements are written with example tables.The tables are really tests.Writing tests as a table is an interesting paradigm
Some tests naturally fit into tablesSome tests require thought to put them in the form of a table
80
The Mechanics
The first row of a FIT table is the title of the table.
This title is the name of something called a “fixture”.
Fixtures are simple programs written by the developers to connect the application to the data in the table.
81
The MechanicsWhen you run a test the fixture gathers the data from the table and calls the appropriate functions in the application.
The fixture then gathers data from the application and compares them with entries in the table, turning them green or red.
82
Acceptance Testing with Fit/FitNesse
The Application InterfaceUI
Fit Fixtures Fit/FitNesse
The ApplicationLogic
The Test-Bus Imperative
84
Many Industries build testability in.
Telecommunications
Manufacturing
Electronic Hardware
85
But not the software industry.
Testability is almost always an afterthought
If it is thought of at all.
86
Untestable Dependencies.
Network
Network
READY
Too often applications directly depend on external and third party resources.
87
Dependency Management
Network
Network
READY
Interfaces isolate the system from external dependencies.
88
Testability!
Tests andSimulations
Tests andSimulations
Tests andSimulations
Measuring Project Status
90
Project Management Tool
When done properly, Acceptance Tests produce valuable data.
0102030405060708090
100
1 3 5 7 9 11
Total AT'sFailing AT'sPassing AT's
91
ATs keep the PM data accurate…
Story Points Remaining
0
100
200
300
400
500
600
1/20/20031/27/20032/3/20032/10/20032/17/20032/24/20033/3/20033/10/2003
Story Points
Velocity
0
10
20
30
40
50
60
1/20/20031/27/20032/3/20032/10/20032/17/20032/24/20033/3/20033/10/2003
Story Points
…and actionable.
92
Iteration Management
93
Agile Software Development Practices
Metaphor
CollectiveOwnership
SustainablePace
ContinuousIntegration
Small Releases
Acceptance Tests
CustomerCollaboration
Scrum
SimpleDesign
Pairing
Test Driven Development
Refactoring
Bowling Game Kata
Copyright 2005 by Object Mentor, IncAll copies must retain this page unchanged.
95
Scoring Bowling.
The game consists of 10 frames as shown above. In each frame the player hastwo opportunities to knock down 10 pins. The score for the frame is the totalnumber of pins knocked down, plus bonuses for strikes and spares.
A spare is when the player knocks down all 10 pins in two tries. The bonus forthat frame is the number of pins knocked down by the next roll. So in frame 3above, the score is 10 (the total number knocked down) plus a bonus of 5 (thenumber of pins knocked down on the next roll.)
A strike is when the player knocks down all 10 pins on his first try. The bonusfor that frame is the value of the next two balls rolled.
In the tenth frame a player who rolls a spare or strike is allowed to roll the extraballs to complete the frame. However no more than three balls can be rolled intenth frame.
96
The Requirements.
+ roll(pins : int)+ score() : int
Game
Write a class named “Game” that has two methods
roll(pins : int) is called each time the player rolls a ball. The argument is the number of pins knocked down.
score() : int is called only at the very end of the game. It returns the total score for that game.
97
A quick design session
+ roll(pins : int)+ score() : int
Game
Clearly we need the Game class.
98
A quick design session
+ roll(pins : int)+ score() : int
Game Frame10
A game has 10 frames.
99
A quick design session
+ roll(pins : int)+ score() : int
Game Frame
- pins : int
Roll10 1..2
A frame has 1 or two rolls.
100
A quick design session
+ roll(pins : int)+ score() : int
Game Frame
Tenth Frame
- pins : int
Roll10 1..2
1
The tenth frame has two or three rolls.It is different from all the other frames.
101
A quick design session
+ roll(pins : int)+ score() : int
Game
+ score() : int
Frame
Tenth Frame
- pins : int
Roll10 1..2
1The score function mustiterate through all theframes, and calculateall their scores.
102
A quick design session
+ roll(pins : int)+ score() : int
Game
+ score() : int
Frame
Tenth Frame
- pins : int
Roll10 1..2
1
next frame The score for a spare or a strike depends on the frame’s successor
103
Begin.
Create a project named BowlingGame
Create a unit test named BowlingGameTest
import junit.framework.TestCase;
public class BowlingGameTest extends TestCase {}
104
Begin.
Create a project named BowlingGame
Create a unit test named BowlingGameTest
import junit.framework.TestCase;
public class BowlingGameTest extends TestCase {}
Execute this program and verify that you get the following error:
No tests found in BowlingGameTest
105
The first test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { public void testGutterGame() throws Exception { Game g = new Game(); }}
106
The first test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { public void testGutterGame() throws Exception { Game g = new Game(); }}
public class Game {}
107
The first test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { public void testGutterGame() throws Exception { Game g = new Game(); }}
public class Game {}
108
The first test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { public void testGutterGame() throws Exception { Game g = new Game(); for (int i=0; i<20; i++) g.roll(0); }}
public class Game {}
109
The first test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { public void testGutterGame() throws Exception { Game g = new Game(); for (int i=0; i<20; i++) g.roll(0); }}
public class Game { public void roll(int pins) { }}
110
The first test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { public void testGutterGame() throws Exception { Game g = new Game(); for (int i=0; i<20; i++) g.roll(0); assertEquals(0, g.score()); }}
public class Game { public void roll(int pins) { }}
111
The first test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { public void testGutterGame() throws Exception { Game g = new Game(); for (int i=0; i<20; i++) g.roll(0); assertEquals(0, g.score()); }}
public class Game { public void roll(int pins) { }
public int score() { return -1; }}
expected:<0> but was:<-1>
112
The first test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { public void testGutterGame() throws Exception { Game g = new Game(); for (int i=0; i<20; i++) g.roll(0); assertEquals(0, g.score()); }}
public class Game { public void roll(int pins) { }
public int score() { return 0; }}
113
The Second test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { public void testGutterGame() throws Exception { Game g = new Game(); for (int i = 0; i < 20; i++) g.roll(0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { Game g = new Game(); for (int i = 0; i < 20; i++) g.roll(1); assertEquals(20, g.score()); }}
public class Game { public void roll(int pins) { }
public int score() { return 0; }}
114
The Second test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { public void testGutterGame() throws Exception { Game g = new Game(); for (int i = 0; i < 20; i++) g.roll(0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { Game g = new Game(); for (int i = 0; i < 20; i++) g.roll(1); assertEquals(20, g.score()); }}
public class Game { public void roll(int pins) { }
public int score() { return 0; }}
- Game creation is duplicated- roll loop is duplicated
115
The Second test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { public void testGutterGame() throws Exception { Game g = new Game(); for (int i = 0; i < 20; i++) g.roll(0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { Game g = new Game(); for (int i = 0; i < 20; i++) g.roll(1); assertEquals(20, g.score()); }}
public class Game { public void roll(int pins) { }
public int score() { return 0; }}
- Game creation is duplicated- roll loop is duplicated
expected:<20> but was:<0>
116
The Second test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
public void testGutterGame() throws Exception { for (int i = 0; i < 20; i++) g.roll(0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { for (int i = 0; i < 20; i++) g.roll(1); assertEquals(20, g.score()); }}
public class Game { private int score = 0;
public void roll(int pins) { score += pins; }
public int score() { return score; }}
- roll loop is duplicated
117
The Second test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
public void testGutterGame() throws Exception { int n = 20; int pins = 0; for (int i = 0; i < n; i++) { g.roll(pins); } assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { for (int i = 0; i < 20; i++) g.roll(1); assertEquals(20, g.score()); }}
public class Game { private int score = 0;
public void roll(int pins) { score += pins; }
public int score() { return score; }}
- roll loop is duplicated
118
The Second test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
public void testGutterGame() throws Exception { int n = 20; int pins = 0; rollMany(n, pins); assertEquals(0, g.score()); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testAllOnes() throws Exception { for (int i = 0; i < 20; i++) g.roll(1); assertEquals(20, g.score()); }}
public class Game { private int score = 0;
public void roll(int pins) { score += pins; }
public int score() { return score; }}
- roll loop is duplicated
119
The Second test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testAllOnes() throws Exception { for (int i = 0; i < 20; i++) g.roll(1); assertEquals(20, g.score()); }}
public class Game { private int score = 0;
public void roll(int pins) { score += pins; }
public int score() { return score; }}
- roll loop is duplicated
120
The Second test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }}
public class Game { private int score = 0;
public void roll(int pins) { score += pins; }
public int score() { return score; }}
- roll loop is duplicated
121
The Second test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); } private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); } public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }}
public class Game { private int score = 0;
public void roll(int pins) { score += pins; }
public int score() { return score; }}
122
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { g.roll(5); g.roll(5); // spare g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }}
public class Game { private int score = 0;
public void roll(int pins) { score += pins; }
public int score() { return score; }}
- ugly comment in test.
123
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { g.roll(5); g.roll(5); // spare g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }}
public class Game { private int score = 0;
public void roll(int pins) { score += pins; }
public int score() { return score; }}
- ugly comment in test.
expected:<16> but was:<13>
124
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { g.roll(5); g.roll(5); // spare g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }}
public class Game { private int score = 0;
public void roll(int pins) { score += pins; }
public int score() { return score; }}
- ugly comment in test.
tempted to use flag to remember previous roll. So design must be wrong.
125
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { g.roll(5); g.roll(5); // spare g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }}
public class Game { private int score = 0;
public void roll(int pins) { score += pins; }
public int score() { return score; }}
- ugly comment in test.
roll() calculates score, but name does not imply that.
score() does not calculate score, but name implies that it does.
Design is wrong. Responsibilities are misplaced.
126
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
// public void testOneSpare() throws Exception {// g.roll(5);// g.roll(5); // spare// g.roll(3);// rollMany(17,0);// assertEquals(16,g.score());// }}
public class Game { private int score = 0;
public void roll(int pins) { score += pins; }
public int score() { return score; }}
- ugly comment in test.
127
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
// public void testOneSpare() throws Exception {// g.roll(5);// g.roll(5); // spare// g.roll(3);// rollMany(17,0);// assertEquals(16,g.score());// }}
public class Game { private int score = 0; private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { score += pins; rolls[currentRoll++] = pins; }
public int score() { return score; }}
- ugly comment in test.
128
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
// public void testOneSpare() throws Exception {// g.roll(5);// g.roll(5); // spare// g.roll(3);// rollMany(17,0);// assertEquals(16,g.score());// }}
public class Game { private int score = 0; private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { score += pins; rolls[currentRoll++] = pins; }
public int score() { int score = 0; for (int i = 0; i < rolls.length; i++) score += rolls[i]; return score; }}
- ugly comment in test.
129
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
// public void testOneSpare() throws Exception {// g.roll(5);// g.roll(5); // spare// g.roll(3);// rollMany(17,0);// assertEquals(16,g.score());// }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; for (int i = 0; i < rolls.length; i++) score += rolls[i]; return score; }}
- ugly comment in test.
130
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { g.roll(5); g.roll(5); // spare g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; for (int i = 0; i < rolls.length; i++) score += rolls[i]; return score; }}
expected:<16> but was:<13>
- ugly comment in test.
131
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { g.roll(5); g.roll(5); // spare g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; for (int i = 0; i < rolls.length; i++) { if (rolls[i] + rolls[i+1] == 10) // spare score += ... score += rolls[i]; } return score; }}
This isn’t going to work because i might not refer to the first ball of the frame.
Design is still wrong.
Need to walk through array two balls (one frame) at a time.
- ugly comment in test.
132
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
// public void testOneSpare() throws Exception {// g.roll(5);// g.roll(5); // spare// g.roll(3);// rollMany(17,0);// assertEquals(16,g.score());// }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; for (int i = 0; i < rolls.length; i++) score += rolls[i]; return score; }}
- ugly comment in test.
133
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
// public void testOneSpare() throws Exception {// g.roll(5);// g.roll(5); // spare// g.roll(3);// rollMany(17,0);// assertEquals(16,g.score());// }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int i = 0; for (int frame = 0; frame < 10; frame++) { score += rolls[i] + rolls[i+1]; i += 2; } return score; }}
- ugly comment in test.
134
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { g.roll(5); g.roll(5); // spare g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int i = 0; for (int frame = 0; frame < 10; frame++) { score += rolls[i] + rolls[i+1]; i += 2; } return score; }}
expected:<16> but was:<13>
- ugly comment in test.
135
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { g.roll(5); g.roll(5); // spare g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int i = 0; for (int frame = 0; frame < 10; frame++) { if (rolls[i] + rolls[i + 1] == 10) // spare { score += 10 + rolls[i + 2]; i += 2; } else { score += rolls[i] + rolls[i + 1]; i += 2; } } return score; }}
- ugly comment in test.
136
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { g.roll(5); g.roll(5); // spare g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int i = 0; for (int frame = 0; frame < 10; frame++) { if (rolls[i] + rolls[i + 1] == 10) // spare { score += 10 + rolls[i + 2]; i += 2; } else { score += rolls[i] + rolls[i + 1]; i += 2; } } return score; }}
-ugly comment in test.-ugly comment in conditional.-i is a bad name for this variable
137
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { g.roll(5); g.roll(5); // spare g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int frameIndex = 0; for (int frame = 0; frame < 10; frame++) { if (rolls[frameIndex] + rolls[frameIndex + 1] == 10) // spare { score += 10 + rolls[frameIndex + 2]; frameIndex += 2; } else { score += rolls[frameIndex] + rolls[frameIndex + 1]; frameIndex += 2; } } return score; }}
-ugly comment in test.-ugly comment in conditional.
138
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { g.roll(5); g.roll(5); // spare g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int frameIndex = 0; for (int frame = 0; frame < 10; frame++) { if (isSpare(frameIndex)) { score += 10 + rolls[frameIndex + 2]; frameIndex += 2; } else { score += rolls[frameIndex] + rolls[frameIndex + 1]; frameIndex += 2; } } return score; }
private boolean isSpare(int frameIndex) { return rolls[frameIndex] + rolls[frameIndex + 1] == 10; }}
-ugly comment in test.
139
The Third test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { private Game g;
protected void setUp() throws Exception { g = new Game(); }
private void rollMany(int n, int pins) { for (int i = 0; i < n; i++) g.roll(pins); }
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { rollSpare(); g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }
private void rollSpare() { g.roll(5); g.roll(5); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int frameIndex = 0; for (int frame = 0; frame < 10; frame++) { if (isSpare(frameIndex)) { score += 10 + rolls[frameIndex + 2]; frameIndex += 2; } else { score += rolls[frameIndex] + rolls[frameIndex + 1]; frameIndex += 2; } } return score; }
private boolean isSpare(int frameIndex) { return rolls[frameIndex] + rolls[frameIndex + 1] == 10; }}
-
140
The Fourth test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { ...
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { rollSpare(); g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }
public void testOneStrike() throws Exception { g.roll(10); // strike g.roll(3); g.roll(4); rollMany(16, 0); assertEquals(24, g.score()); }
private void rollSpare() { g.roll(5); g.roll(5); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int frameIndex = 0; for (int frame = 0; frame < 10; frame++) { if (isSpare(frameIndex)) { score += 10 + rolls[frameIndex + 2]; frameIndex += 2; } else { score += rolls[frameIndex] + rolls[frameIndex + 1]; frameIndex += 2; } } return score; }
private boolean isSpare(int frameIndex) { return rolls[frameIndex] + rolls[frameIndex + 1] == 10; }}
- ugly comment in testOneStrike.
expected:<24> but was:<17>
141
The Fourth test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { ...
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { rollSpare(); g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }
public void testOneStrike() throws Exception { g.roll(10); // strike g.roll(3); g.roll(4); rollMany(16, 0); assertEquals(24, g.score()); }
private void rollSpare() { g.roll(5); g.roll(5); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int frameIndex = 0; for (int frame = 0; frame < 10; frame++) { if (rolls[frameIndex] == 10) // strike { score += 10 + rolls[frameIndex+1] + rolls[frameIndex+2]; frameIndex++; } else if (isSpare(frameIndex)) { score += 10 + rolls[frameIndex + 2]; frameIndex += 2; } else { score += rolls[frameIndex] + rolls[frameIndex + 1]; frameIndex += 2; } } return score; }
private boolean isSpare(int frameIndex) { return rolls[frameIndex] + rolls[frameIndex + 1] == 10; }}
-ugly comment in testOneStrike.-ugly comment in conditional.-ugly expressions.
142
The Fourth test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { ...
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { rollSpare(); g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }
public void testOneStrike() throws Exception { g.roll(10); // strike g.roll(3); g.roll(4); rollMany(16, 0); assertEquals(24, g.score()); }
private void rollSpare() { g.roll(5); g.roll(5); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int frameIndex = 0; for (int frame = 0; frame < 10; frame++) { if (rolls[frameIndex] == 10) // strike { score += 10 + strikeBonus(frameIndex); frameIndex++; } else if (isSpare(frameIndex)) { score += 10 + spareBonus(frameIndex); frameIndex += 2; } else { score += sumOfBallsInFrame(frameIndex); frameIndex += 2; } } return score; }
private int sumOfBallsInFrame(int frameIndex) { return rolls[frameIndex]+rolls[frameIndex+1]; }
private int spareBonus(int frameIndex) { return rolls[frameIndex + 2]; }
private int strikeBonus(int frameIndex) { return rolls[frameIndex+1]+rolls[frameIndex+2]; }
private boolean isSpare(int frameIndex) { return rolls[frameIndex]+rolls[frameIndex+1] == 10; }}
-ugly comment in testOneStrike.-ugly comment in conditional.
143
The Fourth test.import junit.framework.TestCase;
public class BowlingGameTest extends TestCase { ...
public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { rollSpare(); g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }
public void testOneStrike() throws Exception { g.roll(10); // strike g.roll(3); g.roll(4); rollMany(16, 0); assertEquals(24, g.score()); }
private void rollSpare() { g.roll(5); g.roll(5); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int frameIndex = 0; for (int frame = 0; frame < 10; frame++) { if (isStrike(frameIndex)) { score += 10 + strikeBonus(frameIndex); frameIndex++; } else if (isSpare(frameIndex)) { score += 10 + spareBonus(frameIndex); frameIndex += 2; } else { score += sumOfBallsInFrame(frameIndex); frameIndex += 2; } } return score; }
private boolean isStrike(int frameIndex) { return rolls[frameIndex] == 10; }
private int sumOfBallsInFrame(int frameIndex) { return rolls[frameIndex] + rolls[frameIndex+1]; }
private int spareBonus(int frameIndex) { return rolls[frameIndex+2]; }
private int strikeBonus(int frameIndex) { return rolls[frameIndex+1] + rolls[frameIndex+2]; }
private boolean isSpare(int frameIndex) { return rolls[frameIndex]+rolls[frameIndex+1] == 10; }}
-ugly comment in testOneStrike.
144
The Fourth test. ... public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { rollSpare(); g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }
public void testOneStrike() throws Exception { rollStrike(); g.roll(3); g.roll(4); rollMany(16, 0); assertEquals(24, g.score()); }
private void rollStrike() { g.roll(10); }
private void rollSpare() { g.roll(5); g.roll(5); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int frameIndex = 0; for (int frame = 0; frame < 10; frame++) { if (isStrike(frameIndex)) { score += 10 + strikeBonus(frameIndex); frameIndex++; } else if (isSpare(frameIndex)) { score += 10 + spareBonus(frameIndex); frameIndex += 2; } else { score += sumOfBallsInFrame(frameIndex); frameIndex += 2; } } return score; }
private boolean isStrike(int frameIndex) { return rolls[frameIndex] == 10; }
private int sumOfBallsInFrame(int frameIndex) { return rolls[frameIndex] + rolls[frameIndex+1]; }
private int spareBonus(int frameIndex) { return rolls[frameIndex+2]; }
private int strikeBonus(int frameIndex) { return rolls[frameIndex+1] + rolls[frameIndex+2]; }
private boolean isSpare(int frameIndex) { return rolls[frameIndex]+rolls[frameIndex+1] == 10; }}
145
The Fifth test. ... public void testGutterGame() throws Exception { rollMany(20, 0); assertEquals(0, g.score()); }
public void testAllOnes() throws Exception { rollMany(20,1); assertEquals(20, g.score()); }
public void testOneSpare() throws Exception { rollSpare(); g.roll(3); rollMany(17,0); assertEquals(16,g.score()); }
public void testOneStrike() throws Exception { rollStrike(); g.roll(3); g.roll(4); rollMany(16, 0); assertEquals(24, g.score()); }
public void testPerfectGame() throws Exception { rollMany(12,10); assertEquals(300, g.score()); }
private void rollStrike() { g.roll(10); }
private void rollSpare() { g.roll(5); g.roll(5); }}
public class Game { private int rolls[] = new int[21]; private int currentRoll = 0;
public void roll(int pins) { rolls[currentRoll++] = pins; }
public int score() { int score = 0; int frameIndex = 0; for (int frame = 0; frame < 10; frame++) { if (isStrike(frameIndex)) { score += 10 + strikeBonus(frameIndex); frameIndex++; } else if (isSpare(frameIndex)) { score += 10 + spareBonus(frameIndex); frameIndex += 2; } else { score += sumOfBallsInFrame(frameIndex); frameIndex += 2; } } return score; }
private boolean isStrike(int frameIndex) { return rolls[frameIndex] == 10; }
private int sumOfBallsInFrame(int frameIndex) { return rolls[frameIndex] + rolls[frameIndex+1]; }
private int spareBonus(int frameIndex) { return rolls[frameIndex+2]; }
private int strikeBonus(int frameIndex) { return rolls[frameIndex+1] + rolls[frameIndex+2]; }
private boolean isSpare(int frameIndex) { return rolls[frameIndex]+rolls[frameIndex+1] == 10; }}
End
147
Contact Information
Robert C. Martinunclebob@objectmentor.com
Website:www.objectmentor.com
FitNesse:www.fitnesse.org