Decomposing a skill: Test-Driven Development

6 views
Skip to first unread message

Adam Sroka

unread,
Dec 23, 2009, 6:27:25 PM12/23/09
to agile-devel...@googlegroups.com
In the interest of exploring this idea, I am taking a stab at
decomposing TDD into constituent skills that are as close to atomic as
I can manage based on my current practice and understanding. I am
certain that the list is incomplete and doesn't represent every
viewpoint (Or any, other than my own.) Feedback is appreciated.

Here goes, in outline format:

Test-Driven Development

I. Unit Testing

1. Able to run individual modules of working code in isolation to
explore their inputs/outputs and relationships to other modules:
a) from a command line interpreter
b) from an xUnit style test runner
c) from an IDE or object browser of some kind

2. Able to describe expected behavior of individual units of code
and verify them automatically:
a) using assertions to verify pre-/post-conditions and query results
b) using test doubles to hide complexity due to external
dependencies and verify interactions with collaborators

3. Understands the difference between various types of tests and
when/why to use each:
a) functional/acceptance tests which exercise the system end-to-end
b) integration tests which exercise multiple modules interacting together
c) unit tests which exercise a single behavior of a single
module in isolation

II. Refactoring

1. Able to perform small, behavior-preserving changes in a
disciplined manner:
a) using automated tools
b) manually, using the step-by-step procedures outlined in the
refactoring literature
c) combining several small automated or well understood
refactorings to effect a larger change

2. Able to quantify/verify the behavior of the code prior to and
after changing it so as to validate its correctness

III. Simple Design

1. Understands basic principles of design:
a) OO/Functional/Procedural design principles
b) Decomposition/Divide-and-Conquer, Information hiding
c) Clean Code

2. Able to recognize indications of good/bad design in working code

3. Able to restructure code opportunistically to remove code smells
and to bolster/enhance the design

4. Able to Code by Intention, designing an API by imagining how it
will be used

Adam Sroka

unread,
Dec 23, 2009, 6:39:02 PM12/23/09
to agile-devel...@googlegroups.com
This is what I was afraid of; I missed the most important part. See below:

IV. The TDD Cycle

1. Able to create a simple test which compels a vanishingly small
bit of implementation in order to pass

2. Has sufficient mastery of the language to create a small, simple
implementation

3. Able to apply Simple Design and Refactoring to simplify and
improve the design without breaking the test

4. Able to repeat the Red-Green-Refactor cycle quickly in small,
rapid increments

John Stoneham

unread,
Dec 25, 2009, 3:19:12 PM12/25/09
to agile-devel...@googlegroups.com
This is a great start. It seems to me there's a bit of, for lack of a
better word, -intention- missing, though.

This is something people new to unit testing or TDD struggle with -
the "what test do I write next?" problem. It's the ability to see
several moves in advance, to know where you're going and drive in that
direction by writing the correct tests, to see which tests are still
missing, to get across the river one stroke at a time. All while
listening to what the code and tests are trying to tell you about your
design.

I like your module I, Unit Testing, because it represents the raw
technical skill to do this sort of work independent of intention or
direction. II.1.c (" combining several small automated or well
understood refactorings to effect a larger change") gets at what I'm
looking for, but in a refactoring context and not a TDD one.

I suggest adding a #5 to The TDD Cycle:

5. Able to combine and direct small, rapid increments to evolve the
design and implement larger features, attending to code smells and
design improvement opportunities as they appear

(It could use some wordsmithing, I'm sure.)

- John Stoneham

Adam Sroka

unread,
Dec 25, 2009, 7:40:08 PM12/25/09
to agile-devel...@googlegroups.com
On Fri, Dec 25, 2009 at 12:19 PM, John Stoneham <ly...@lyrically.net> wrote:
> This is a great start. It seems to me there's a bit of, for lack of a
> better word, -intention- missing, though.
>

Good point. I think I'd put that under the Simple Design heading.
Maybe something like:

5. Able to build strategically towards a complete solution while
managing complexity
a) understanding the difference between essential and accidental complexity
b) recognizing and applying software design patterns
c) using refactoring to change strategies on the fly

> This is something people new to unit testing or TDD struggle with -
> the "what test do I write next?" problem. It's the ability to see
> several moves in advance, to know where you're going and drive in that
> direction by writing the correct tests, to see which tests are still
> missing, to get across the river one stroke at a time. All while
> listening to what the code and tests are trying to tell you about your
> design.
>

Perhaps I need to add something to the Unit Testing section:

4. Able to select test cases for maximum coverage/effect
a) tests that reveal the intention of the code by exercising the
API as it is meant to be used
b) tests that cover boundary conditions and other areas of complexity

> I like your module I, Unit Testing, because it represents the raw
> technical skill to do this sort of work independent of intention or
> direction. II.1.c (" combining several small automated or well
> understood refactorings to effect a larger change") gets at what I'm
> looking for, but in a refactoring context and not a TDD one.
>
> I suggest adding a #5 to The TDD Cycle:
>
> 5. Able to combine and direct small, rapid increments to evolve the
> design and implement larger features, attending to code smells and
> design improvement opportunities as they appear
>

I really meant for that to be covered by #3 (The "refactor" in
red-green-refactor.) Did my addition to Simple Design account for what
was missing, or do you think I need something more?

John Stoneham

unread,
Dec 25, 2009, 8:23:42 PM12/25/09
to agile-devel...@googlegroups.com
On Fri, Dec 25, 2009 at 7:40 PM, Adam Sroka <adam....@gmail.com> wrote:
> 5. Able to build strategically towards a complete solution while
> managing complexity
>   a) understanding the difference between essential and accidental complexity
>   b) recognizing and applying software design patterns
>   c) using refactoring to change strategies on the fly

I like this content, but I'm having trouble placing this under a
Simple Design heading. What about Evolutionary Design instead?

> 4. Able to select test cases for maximum coverage/effect
>   a) tests that reveal the intention of the code by exercising the API as it is meant to be used
>   b) tests that cover boundary conditions and other areas of complexity

I like this.

> I really meant for that to be covered by #3 (The "refactor" in
> red-green-refactor.) Did my addition to Simple Design account for what
> was missing, or do you think I need something more?

I think it ought to be touched on in both sections in some way - both
refactoring and TDD are activities that are performed with a direction
in mind via small steps. In TDD, I think we ought to capture the
gradual accumulation of functionality that becomes larger features.
How about something simpler than my earlier suggestion:

5. Able to build up small, rapid changes to form larger features while
maintaining a simple design.

(but I -really- wanted to get to use the word 'agglutinate'...)

- John Stoneham

Elizabeth Keogh

unread,
Dec 26, 2009, 7:31:56 AM12/26/09
to agile-devel...@googlegroups.com
On Wed, Dec 23, 2009 at 11:27 PM, Adam Sroka <adam....@gmail.com> wrote:
> In the interest of exploring this idea, I am taking a stab at
> decomposing TDD into constituent skills that are as close to atomic as
> I can manage based on my current practice and understanding.

Hi Adam,

I use the Dreyfus Model to coach all kinds of skills and to provide a
roadmap for people to improve. These are the models I use for TDD and
Acceptance Testing, from a developer-centric view. In my model the
"Competent" level allows an individual to be independently successful
with that skill, without relying on help from anyone else, though they
may still experience some pain.

I'm sharing them with you because I find that the idea of a journey
when picking up skills is powerful, and in case you find something
useful in my BDD-centric language. It's not quite complete - you've
got some ideas I'd like to add to this model, if you don't mind.


TDD
Novice
Writes tests before code, most of the time
Beginner
Uses tests in combination with IDE to drive code
Tests fail before code is written
Adds tests to legacy code
Competent
Gives meaningful names to test methods
Tests provide documentation for the classes
Uses mocks or other test doubles to decouple dependencies
Knowledgeable
Refactors tests
Uses tests to describe why the code is valuable
Uses tests as examples of how to use the code effectively
Drives out responsibilities and elegant design using TDD
Ensures that tests are independent and robust
Expert
Uses TDD to derive role-based interfaces
Drives code from outside-in
Understands that TDD isn't just about testing
Understands the value of testing separately to TDD.


Acceptance Testing
NB: A UI - user interface - is the point at which the code provides
value to its customer. Its customer may be a third-party application,
another system, etc. - it doesn't have to be an actual GUI.
Novice
Writes acceptance tests
Beginner
Writes acceptance tests before writing the code
Competent
Ensures acceptance tests are legible and maintainable
Talks to QAs about scenarios before starting development
Uses acceptance tests to investigate bugs
Knowledgeable
Does not use production code when writing tests
Tests from the UI
Writes simulators for 3rd-party applications
Uses scenarios in conversation to establish a shared
understanding of the domain
Uses acceptance tests to reach a shared understanding with QAs
and the business of what 'done' looks like
Expert
Merges automated tests to minimise set-up costs, using one as
the context of another to form regression tests
Can establish different environments for acceptance testing
and ensure that tests work in all
Can manage set-up of complex scenarios pragmatically, using
the UI or data-driven contexts as appropriate.

Cheers,
Liz.

--
Elizabeth Keogh
l...@lunivore.com
http://lizkeogh.com
http://lunivore.com

Adam Sroka

unread,
Dec 26, 2009, 1:56:31 PM12/26/09
to agile-devel...@googlegroups.com
Hi Liz:

I don't know much about the Dreyfus Model. It came up at the workshop
and I read the wikipedia article about it.

I think your list is great. I can definitely see how a person could
learn TDD by working through those steps with some gentle guidance.
You are certainly welcome to use things from my list to modify yours.
Possibly, we could also use your list as a "course" to measure against
the inventory.

In the other threads (Not sure if you've been reading them all) I
noticed a pattern:

A: Here is skill X including "steps to mastery."
B: I don't think those are the right steps. I would add foo. I don't
think we need to include bar.
C: I think we need bar. I don't do foo.
D: Perhaps there is more than one correct way to do it. Perhaps we
shouldn't even describe a single way to do it.

I have been saying that I think we have to say *something* about how
to do it, otherwise we have no idea what people will do.

Charlie Poole has been saying that he thinks some of the skills we are
talking about (And TDD is one of them) are too course grained. That in
fact TDD must constitute numerous smaller skills which could be
individually described.

That was the idea behind my list. Could I come up with a sort of story
map of independently valuable skills that together constitute TDD? If
I could, could we use it to inform the types of skills and level of
detail that the Skills Inventory should contain? Could we then do the
same thing for other high level skills?

> --
> ________________________________________________________________
>
> Much of the discussion in the group is predicated on several resources summarized at http://sites.google.com/site/agileskillsprojectwiki/home   Please review this regularly.  To get editing permissions for the wiki, send a note to both d.andre...@gmail.com and redho...@gmail.com .
>
> ________________________________________________________________
> You received this message because you are subscribed to
> the "Agile Developer Skills" group.
>
> To unsubscribe from this group, send email to
> agile-developer-s...@googlegroups.com
>
> For more options, visit this group at
> http://groups.google.com/group/agile-developer-skills?hl=en?hl=en
>

Esko Luontola

unread,
Dec 26, 2009, 7:47:35 PM12/26/09
to Agile Developer Skills
Here are some decomposed parts of TDD that come into my mind, in
addition to what has already been said:

- How to write testable code. As Miško Hevery says in one of the links
below, "Well there are no tricks to writing tests, there are only
tricks to writing testable code. If I gave you testable code you would
have no problems writing a test for it."

Writing testable code can further be decomposed into things like using
dependency injection, avoiding global mutable state, writing small
focused classes (Single Responsibility Principle, Open Closed
Principle etc.) and so on.

I think that test-first forces the developer to write testable code,
and doing test-after is hard because you don't yet know how to write
testable code. If you have experience in TDD, then you could write
testable code even if you would sometimes work test-after.

Links for learning more:
http://googletesting.blogspot.com/2008/08/by-miko-hevery-so-you-decided-to.html
http://googletesting.blogspot.com/2008/11/guide-to-writing-testable-code.html
http://googletesting.blogspot.com/2008/11/clean-code-talks-dependency-injection.html
http://googletesting.blogspot.com/2008/11/clean-code-talks-global-state-and.html


- Using test doubles. A TDD practitioner should know when to use test
doubles and what kinds of test doubles fit a situation the best.

He should also have experience about the mock object frameworks that
are available for his platform, and should know the difference between
mock frameworks (JMock, EasyMock) and spy frameworks (Mockito).

Some links:
http://code.google.com/testing/TotT-2008-06-12.pdf
http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html
http://martinfowler.com/articles/mocksArentStubs.html
http://hamletdarcy.blogspot.com/2007/10/mocks-and-stubs-arent-spies.html


- Writing self-documenting tests which specify what is the desired
behaviour of the system (see Behaviour Driven Development).

When a test fails, there are three options: (1) the code is broken and
needs to be fixed, (2) the test is broken, but still needed, and it
needs to be fixed, (3) the test is not anymore needed and needs to be
removed. To be able to know what to do in each situation, the tests
should have long descriptive names, which explain the intent behind
the test. Each test should tell that what is the behaviour being
specified by the test.

The style that I have found to work for me is to write "tests as
specification" (for examples, see http://github.com/orfjackal/tdd-tetris-tutorial
- the 'beyond' branch contains the most complete code). I have also
seen many people using a style which I call "tests as examples", for
example as seen in Uncle Bob's Bowling Game Kata, which IMO does not
as well document the intent behind each test (but in some situations
is easier to write and less verbose).

Some links:
http://dannorth.net/introducing-bdd
http://techblog.daveastels.com/2005/07/05/a-new-look-at-test-driven-development/
http://techblog.daveastels.com/files/BDD_Intro.pdf


- It would also be useful to document some of the common stumblings
that learners of TDD face. Here are some things that I've noticed
(from myself and my students) to be hard to do when you're new to TDD:

* Being disciplined enough to always write a failing test first. The
temptation of returning to the old ways is high.

* Proceeding in small steps. Each new test should specify only a
little bit new behaviour, and passing a new test should not take a
long time. A balance is needed between writing small enough steps
(otherwise making the test pass will be hard) and not writing too
small steps (otherwise the progress will be slow).

* Knowing which test to write next. BDD's "what is the next most
important behaviour" question can help. Also Kent Beck's
Simplification strategy (http://www.infoq.com/presentations/responsive-
design) is useful when writing the first tests for a new class.

* Writing tests which serve as good documentation. Choosing
descriptive names for the tests.

* Writing tests which are decoupled from the implementation details.
If you need to change the tests when refactoring, it might be a code
smell that the tests are too tightly coupled to some implementation
details. It's better to write tests, which are focused around the
desired behaviour, and not the implementation. For example if you have
a test "testSetFoo" and then you remove/rename the "setFoo" method as
part of a refactoring (for example change it to be a constructor
parameter), then the test also needs to be changed, even though the
behaviour provided by the system stays the same.

D.André Dhondt

unread,
Apr 15, 2010, 3:12:46 PM4/15/10
to agile-devel...@googlegroups.com
Here's some work-in-progress, in attempts to see what it means to define a skill with Forces, Influences, and Intentions.  I'm getting lost, quickly, and don't know if there's anything salvageable here.  What do you think?

Test-Driven Development (TDD)

Definition
Test-Driven Development (TDD) is a way of driving the design of code by writing a test which expresses what you intend the code to do, making that test pass, and continuously refactoring to keep the design as simple as possible.

Forces
[a force names something that we need to be mindful of, and explains what it influences]
rapid feedback decreases a debugging mindset
small, cohesive units increase comprehension of code
code debt increases the time it takes to get legacy code under test
timeboxes limit how much code can be put under test harness 
expressivity of underlying code (dsl) shortens time to write tests
the big picture helps us envision what test to write next
test after instead of test first may reduce testability or increase cost of tests or decrease test coverage 
failing tests may increase effort of getting the next new test to pass
examples vs behavior
outside in, inside out
automated or manual unit tests
setup/tear

Influences
[an influence is something affected by using more of one of the forces]
some tools launch tests automatically whenever the code is changed or compiled
slow tests reduce feedback
test doubles can help isolate the Subject-Under-Test from highly coupled dependencies
debugging is slow and error-prone

Intentions
[an intention is a desired outcome of applying the skill]
TDD makes the intention of the code abundantly clear.
TDD can apply at multiple levels, e.g., Customer Tests, Integration Tests, Unit Tests, and forces us to come up with a clear definition of success before implementing the code 


[below are the merged comments from the unfinished discussion in December]
4. Able to select test cases for maximum coverage/effect
  a) tests that reveal the intention of the code by exercising the
API as it is meant to be used
  b) tests that cover boundary conditions and other areas of complexity

II. Refactoring

  1. Able to perform small, behavior-preserving changes in a
disciplined manner:
     a) using automated tools
     b) manually, using the step-by-step procedures outlined in the
refactoring literature
     c) combining several small automated or well understood
refactorings to effect a larger change

  2. Able to quantify/verify the behavior of the code prior to and
after changing it so as to validate its correctness

III. Simple Design

  1. Understands basic principles of design:
     a) OO/Functional/Procedural design principles
     b) Decomposition/Divide-and-Conquer, Information hiding
     c) Clean Code

  2. Able to recognize indications of good/bad design in working code

  3. Able to restructure code opportunistically to remove code smells
and to bolster/enhance the design

  4. Able to Code by Intention, designing an API by imagining how it
will be used

IV. The TDD Cycle
  1. Able to create a simple test which compels a vanishingly small
bit of implementation in order to pass
  2. Has sufficient mastery of the language to create a small, simple
implementation
  3. Able to apply Simple Design and Refactoring to simplify and
improve the design without breaking the test
  4. Able to repeat the Red-Green-Refactor cycle quickly in small,
rapid increments
5. Able to build strategically towards a complete solution while
managing complexity
  a) understanding the difference between essential and accidental complexity
  b) recognizing and applying software design patterns
  c) using refactoring to change strategies on the fly


TDD
   Novice
       Writes tests before code, most of the time
   Beginner
       Uses tests in combination with IDE to drive code
       Tests fail before code is written
       Adds tests to legacy code
   Competent
       Gives meaningful names to test methods
       Tests provide documentation for the classes
       Uses mocks or other test doubles to decouple dependencies
   Knowledgeable
       Refactors tests
       Uses tests to describe why the code is valuable
       Uses tests as examples of how to use the code effectively
       Drives out responsibilities and elegant design using TDD
       Ensures that tests are independent and robust
   Expert
       Uses TDD to derive role-based interfaces
       Drives code from outside-in
       Understands that TDD isn't just about testing
       Understands the value of testing separately to TDD.


???
????



--
D. André Dhondt
mobile: 001 33 671 034 984
twitter: adhondt   http://dhondtsayitsagile.blogspot.com/

Support low-cost conferences -- http://AgileTour.org/
If you're in the area, join Agile Philly http://www.AgilePhilly.com
Mentor/be mentored for free: the Agile Skills Project http://www.AgileSkillsProject.org/
Reply all
Reply to author
Forward
0 new messages