Test driven development

A

ajcppmod

Hi

Sorry if this is a bit off topic but as unit testing is such a
cornerstone of python development I thought a few of you may be able
to share your knowledge/experiences.

I like the concept of TDD but find it difficult to put into practice
most of the time. I think this primarily because I tend to like top-
down development and functional/object decomposition and TDD feels
more like a bottom-up approach.

So my question is when approaching a project that you want to employ
test driven development on how and where do you start? And also if
anyone uses top-down design with TDD I would be interested in how you
do it (does it involve lots of mock objects/ is the first test you
write the last one to pass)?

Thanks

Andy
 
N

Neil Cerutti

Hi

Sorry if this is a bit off topic but as unit testing is such a
cornerstone of python development I thought a few of you may be able
to share your knowledge/experiences.

Test driven development, simplistically, means to write the tests
before you write the code.
I like the concept of TDD but find it difficult to put into practice
most of the time. I think this primarily because I tend to like top-
down development and functional/object decomposition and TDD feels
more like a bottom-up approach.

It should work with any unit of development, although it can be hard
to usefully test high-level functionality that depends on currently
unimplemented underware.
So my question is when approaching a project that you want to employ
test driven development on how and where do you start? And also if
anyone uses top-down design with TDD I would be interested in how you
do it (does it involve lots of mock objects/ is the first test you
write the last one to pass)?

The system I've adopted is to use inline doctests for the simplest,
tutorial-like examples, with "outline" unit tests composd to verify
implementation details and to probe bounderies. Typically, I write a
doctest, then the routine, and finally the unit tests.

Top-down should work fine with test-driven, although your highest
level tests will fail until your low-level tests pass. All the failing
tests might be kind of depressing, though. Personally, I haven't
really given top-down a fair shake, so I don't know which approach
reveals my stupid design mistakes faster.
 
A

A.T.Hofkamp

I like the concept of TDD but find it difficult to put into practice
most of the time. I think this primarily because I tend to like top-
down development and functional/object decomposition and TDD feels
more like a bottom-up approach.

It is not bottom-up imho.
The main difference imho is in abstract, far-away goals versus running code for
a small concrete sub-set of functionality in the short-term.

How you provide that "running code for a small concrete sub-set of
functionality in the short-term" is not defined by the approach.
So my question is when approaching a project that you want to employ
test driven development on how and where do you start? And also if

1. Define a small step of extended functionality that you can finish by the
end of the week.
2. Write tests that should run after implementing the functionality.
3. Write code that make the tests work.
4. goto 1.
anyone uses top-down design with TDD I would be interested in how you
do it (does it involve lots of mock objects/ is the first test you
write the last one to pass)?

No, with TTD there is only one goal, namely increasing the percentage of passed
tests to 100%.
(which you then break again by doing steps 1 and 2, and fix again in step 3)


TTD is about DESIGNing for WHAT YOU NEED TODAY, not for what you might need
tomorrow.

You may want to do a small top-down design (and next week another slightly
larger one, and another slightly larger one in 2 weeks, etc), but you should
NOT DESIGN FOR MORE FUNCTIONALITY THAN WHAT YOU NEED for all tests to pass.


Sincerely,
Albert

PS The above is purely from a TTD perspective.

I personally do not really think you can SOLVE ALL PROBLEMS in the most
efficient way using TTD. On the other hand, the concrete, short-term
approach does produce running code fast for the implemented sub-set of
functionality, which may be nice in a project.
 
R

Roy Smith

So my question is when approaching a project that you want to employ
test driven development on how and where do you start? And also if
anyone uses top-down design with TDD I would be interested in how you
do it (does it involve lots of mock objects/ is the first test you
write the last one to pass)?

I've been a big fan of TDD for a few years.

I don't always use it. When working with legacy code, sometimes the pain
of getting things into a test harness exceeds the effort I'm able or
willing to put into it right now. The other times I don't use it is when
I'm "just doing some simple little thing" (which inevitably grows into
something bigger than originally anticipated). In all cases, I often find
that code I ended up writing is less well documented, less well tested, and
more buggy.

You've hit the nail on the head with the top-down, bottom-up issue. TDD
(at least in my mind) encourages bottom-up design, because it forces you to
have working code on day one. This is not always good. So, the key (as
you pointed out) to mixing TDD with top-down, is to use mock objects a lot.
But, this is a good thing; it forces you to write classes which can be
tested in isolation. This no only makes for better tested code, but often
leads to cleaner design.

Now, to drag this thread back to something apropos to Python, the good news
is that Python makes it easy to work with mock objects. Often, all you
need to do is "import myTestingFoo as Foo" and it all just works. Since
functions and classes are first-class objects, it's easy to pass them
around. Since Python uses duck typing, you don't have to make sure your
test class inherets from anything in particular. It all just works.
 
V

Virgil Dupras

Hi

Sorry if this is a bit off topic but as unit testing is such a
cornerstone of python development I thought a few of you may be able
to share your knowledge/experiences.

I like the concept of TDD but find it difficult to put into practice
most of the time. I think this primarily because I tend to like top-
down development and functional/object decomposition and TDD feels
more like a bottom-up approach.

So my question is when approaching a project that you want to employ
test driven development on how and where do you start? And also if
anyone uses top-down design with TDD I would be interested in how you
do it (does it involve lots of mock objects/ is the first test you
write the last one to pass)?

Thanks

Andy

I know what you mean by top-down vs. bottom-up and I used to have the
same dilemma, but now I would tend to agree with Albert. Your issue
with top-down or bottom-up is not relevant in TDD. The only thing that
is relevant is to reach your current milestone as soon as possible,
without caring about what you're going to do in the milestone after
that.

Not so long ago, I took the "bottom-up" approach to TDD, which was a
mistake because it leads to over-engineering (the end result is not so
bad since it's over-engineering that has good test coverage :) )

Roy: While mocking is good to have tests well organized (instead of
having a huge pile of tests at the highest level), "over-
mocking" (mocking everything, early) leads to, I think, a design that
is too rigid. What if a new spec reveals that the current design of a
large subset of your classes has to be re-done? All your mocking and
the test below them, they all have to be "brought up" to the highest
level of tests so you can re-organize your code. Since doing this is a
lot of work, and usually messing with tests is a lot more dangerous
than messing with the code itself, you would tend to stay with your
old design, even if it's not the optimal design for the task you need
to do.

Virgil
 
R

Roel Schroeven

Virgil Dupras schreef:
I know what you mean by top-down vs. bottom-up and I used to have the
same dilemma, but now I would tend to agree with Albert. Your issue
with top-down or bottom-up is not relevant in TDD. The only thing that
is relevant is to reach your current milestone as soon as possible,
without caring about what you're going to do in the milestone after
that.

Not so long ago, I took the "bottom-up" approach to TDD, which was a
mistake because it leads to over-engineering (the end result is not so
bad since it's over-engineering that has good test coverage :) )

I don't regularly use TDD yet, and one of the reasons is that in many
cases I'm unsure exactly how to use it in practice. I read "Test-driven
development - A practical guide" (and I should re-read), but I feel it
doesn't help my much in everyday situations. Somehow the examples in the
book don't match very well with how I code (and I admit that perhaps the
problem is more with me than with the book).

One of the problems I have is something like what Andy describes: I need
a function spam(), so I write tests for it. Then I start implementing
the function and see that I need to write functions ham() and eggs().
Should I write unit tests for ham() and eggs(), or do I rely on the
tests for spam()? If I don't write them, doesn't that make it more
difficult to find out why the tests for spam() fail?

Speaking about over-engineering, when I do TDD along the lines of the
book I mentioned, I always feel that I'm over-engineering the tests. It
all feels very unnatural though I'm convinced it shouldn't be like that.

Can someone suggest other books to read about the subject, ideally
something more focused on Python or C++ rather than Java?

--
The saddest aspect of life right now is that science gathers knowledge
faster than society gathers wisdom.
-- Isaac Asimov

Roel Schroeven
 
V

Virgil Dupras

Virgil Dupras schreef:



I don't regularly use TDD yet, and one of the reasons is that in many
cases I'm unsure exactly how to use it in practice. I read "Test-driven
development - A practical guide" (and I should re-read), but I feel it
doesn't help my much in everyday situations. Somehow the examples in the
book don't match very well with how I code (and I admit that perhaps the
problem is more with me than with the book).

One of the problems I have is something like what Andy describes: I need
a function spam(), so I write tests for it. Then I start implementing
the function and see that I need to write functions ham() and eggs().
Should I write unit tests for ham() and eggs(), or do I rely on the
tests for spam()? If I don't write them, doesn't that make it more
difficult to find out why the tests for spam() fail?

Speaking about over-engineering, when I do TDD along the lines of the
book I mentioned, I always feel that I'm over-engineering the tests. It
all feels very unnatural though I'm convinced it shouldn't be like that.

Can someone suggest other books to read about the subject, ideally
something more focused on Python or C++ rather than Java?

--
The saddest aspect of life right now is that science gathers knowledge
faster than society gathers wisdom.
   -- Isaac Asimov

Roel Schroeven

I also have a book about TDD and it never was of much help either. All
techniques come from the initial workflow: Red - Green - Refactor.

The problem you describe is a tricky problem. The way I feel it should
be solved is:

- Write spam() (and its tests, of course).
- Then, at some point, in the re-factor phase, you split extract the
function ham() from spam(). Alright, do it and keep the tests as is
(all on spam()).
- Then at some other point, you extract eggs(). same thing.
- After a while (I don't know, 3 or 4 milestones), when it becomes
clear that ham() and eggs() are there to stay, start moving your tests
away from spam() by just mocking ham() and eggs() and just make sure
that spam() call them with the right arguments. The tests you had on
spam() that were relevant to ham() and eggs() are just performed
directly on them now.

What I've been saying in my other message is: Don't do that too early,
because if it turns out that ham() and eggs() is not the optimal way
to do it, but foo() bar() and baz() is, it is *much* easier to do a
safe re-factoring with high level tests.
 
R

Roel Schroeven

Virgil Dupras schreef:
I also have a book about TDD and it never was of much help either. All
techniques come from the initial workflow: Red - Green - Refactor.

The problem you describe is a tricky problem. The way I feel it should
be solved is:

- Write spam() (and its tests, of course).
- Then, at some point, in the re-factor phase, you split extract the
function ham() from spam(). Alright, do it and keep the tests as is
(all on spam()).
- Then at some other point, you extract eggs(). same thing.
- After a while (I don't know, 3 or 4 milestones), when it becomes
clear that ham() and eggs() are there to stay, start moving your tests
away from spam() by just mocking ham() and eggs() and just make sure
that spam() call them with the right arguments. The tests you had on
spam() that were relevant to ham() and eggs() are just performed
directly on them now.

What I've been saying in my other message is: Don't do that too early,
because if it turns out that ham() and eggs() is not the optimal way
to do it, but foo() bar() and baz() is, it is *much* easier to do a
safe re-factoring with high level tests.

That sounds reasonable. I'm getting the impression that reading that
book led me to a too strict approach.

I guess I just need to try somewhat harder to use TDD in my daily
coding. Apart from books, are there other resources that can help
beginners with TDD? Mailing lists, forums, newsgroups possibly?

--
The saddest aspect of life right now is that science gathers knowledge
faster than society gathers wisdom.
-- Isaac Asimov

Roel Schroeven
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top