views:

497

answers:

7

Hello.

Even though there are plenty introductions to TDD (even in PHP, my primary programming language now), I'm feeling a bit stuck about it.

My friend introduced me TDD a while ago, but I wasn't actually able to understand it - what's the point in writing tests to everything first - even the simplest tasks - when in the end, it looks it's more efficient to test only if something doesn't work, although it might be much harder to locate the bug (this could be solved through VCS, no?).

So is there any introduction, with dead-simple examples and arguments behind "pros"?

Something like "yeah, it's better, because you have better design - look at this example..."

Also, it would be nice if this simple examples were in php (should be possible) or in as3 (idk...).

Thanks.

EDIT: I understand that you create "test" and then the function, that should pass the test. But how you say to language, what results should it have? To me it looks that you do some function... debug it (the same way as you debug in no test driven dev.) and call it test and do it again... Or?

EDIT 2: Oh... Also, is this applicable to MVC approach? Or rather, CodeIgniter Newbie MVC? :D

EDIT 3: Read a bunch of posted and googled advices, plans, ways and tutorials. I am still not entirely into TDD - I mostly have pretty good visualisation and I think (hope?) I write pretty maintable code, so i still see it as an extra work...

+1  A: 

I personally find the article on wikipedia to be a pretty good introduction.

klausbyskov
+5  A: 

I found the tutorial on Simpletest (PHP) to be really useful. It was 'the TDD launchpad' for me.

See: http://www.simpletest.org/en/start-testing.html

I'd:

  1. Set aside a couple of hours
  2. Get a crude simpletest script working
  3. Open your mind - forget your current dev habits and ways of working for a brief time
  4. ...and just go with the flow - following the instructions and ALL the tutorials, all the way to the end, on the simpletest.org site

Then - in the near future

  1. Start adopting the practices - but gradually. Don't try and do it all at once.
  2. Set up a test folder to sit with one of your projects and start adding a few test cases

If you start doing the steps above now. By this time tomorrow you will definitely fully understand why TDD is so handy.

UPDATE: A note about "1. Set aside a couple of hours" - this is probably the hardest thing to do. Because:

  • If you are putting out feelers to consider the benefits of TDD testing - its probably a symptom that:

    • Your dev work is taking a long time.
    • Which means you have no spare time to set aside to learn about TDD.
    • Which means your dev work is taking a long time.
    • Which means you have no spare time to set aside to learn about TDD.
    • ....etc
JW
Read through it... And it still bugs me, that I can't somehow figure out what is the tested function, what runs the test and what decides about the results... I think I need to read slower...
Adam Kiss
you need to 'do it' rather than read about it. i read loads about it for ages, then one day i went all the way through those tutorials to the end and wham! I go it. it all clicked.
JW
+8  A: 

One of the reasons why this is an effective means to develop is not just writing tests for new code, but those tests remain and are run against existing code, and thus form a regression test.

That means that as your project develops, people take it over or contribute, and it changes, the tests continue to determine whether your code is still running as expected.

The code that you've written that is sufficiently simple not to have to test may well expand and become more complex over time (to take care of edge cases, new requirements etc.). If you're still running your tests (and you should be -via continuous integration or other means) they'll confirm that however much refactoring you do, the code still works as it did on day 1.

Brian Agnew
+1  A: 

The point of writing the unit test first is that by doing this you are writing the specification of your software which seems very natural to do before writing the software itself. How will a given function respond to a given input? If you later decide to change the implementation you won't have to manually retest the whole application.

Darin Dimitrov
Not only the specification - the *executable* specification!
Carl Manaster
+3  A: 

Like most methodologies, you have to live it to understand what it's about, its limitations and its advantages. I recently had to write a client server application and after reading Kent Beck's seminal book (TDD by example) on the subject decided to try to use the methodology. I have a question regarding this which was commented on quite well by a lot of people over here but here is my summary.

Cons

  • It takes much longer. Atleast for me.
  • It requires a lot of restructuring. This might be an advantage but there are pieces of code which I feel are convoluted just because I need to test pieces separately.
  • You can get carried away with writing too many tests.
  • You focus on the trees instead of the forest and have to actively guard yourself from losing focus.

Pros

  • No fear of rewriting pieces. This is probably the best part since I have a programatically verifiable specification of my requirements and it's easy to check after a coding session.
  • Modularity. My functions are small, classes are well contained and modules are manageable. You do require some skill to avoid ravioli code though.
  • Documentation. The tests and their names give me a good chance to structure the docs properly.
  • Rethinking structure. Having to test each layer properly forces me to make solid decisions about what the pieces are and how they communicate. This led to much neater design and a much more understandable terminology.

I'd recommend the book. It's a good read and what will give you the real deal on the methodology. It's not panacea (contrary to what the agile fanatics might say). You can't automatically not worry about the hard decisions just because you're doing TDD. It might seem a little unnatural since you have to think of tests upfront but that's just teething trouble. I'd stay away from "Dead simple" intros you find on the web because they probably miss out on important details.


Update based on comment: Apropos, coming up with smart designs up front and changing them as you go along, that's how I worked till I tried this. The main differences were that the structure I came up with was smarter than usual since I had continuous refinable feedback (eg. you can't test this, it's too monolithic-break it down, these two things are conceptually separate - see how the tests are mashed up etc.) and that the 'dev' steps I took were small enough to throw away without the guilt of 'wasting time' creeping on me. Basically, the 'dev' stage was more systematic and I had verifiable ways to saying what I'd done (tests either pass or fail).

Noufal Ibrahim
Isn't better to take more time to come up with structure, really smart structure where you can edit small parts of it, when you have some idea during dev?
Adam Kiss
I've updated my answer with a response to you.
Noufal Ibrahim
Great update, but... (A lot of buts, i know) isn't it's biggest point then in pushing you to be less monolithic and more conceptual? So, when you believe you're conceptual enough, you have just more work to do, isn't that true? :D
Adam Kiss
I'm assuming you mean the opposite of "monolithic" when you say conceptual. The methodology drives you to become conceptual in small leaps with verifiable ways of measuring improvement. It will get you there faster than a complete upfront design. This is especially useful if (as is often the case in the real world), specifications are fluid and varying.
Noufal Ibrahim
one of the cons for me: * sometimes it puts me off refactoring something - Because all my test scripts/filenames/paths mirrored my domain classes - i'd sometimes think.oO(oh no! I'd have to refactor lots of tests/test files if i changed that)When i first started i was ultra fanatical and made a test before *every* class which lead to alot of brittle tests. Now i am more task focused...ie. i tend to make one test script file mirroring the location of a domain abstract class and inside put tests for the important concrete children.
JW
@Noufal: Under conceptual I thought you have the whole in concept and it's modules in the head (or on the paper) rather than pushing everything into one object/function/file.
Adam Kiss
+1  A: 

One way of phasing in tests is to only write a test when you start debugging a function. It means that tests are made first for the functions that need them most. That's usually enough to noticeably improve development times.

That's not test-driven development, but it will get you comfortable with writing tests and running automated test suites.

Automated test suites are great! They're separate scripts/programs that you run which go through all the tests you've written (the test suite) and tell you which ones fail. It's much faster to run the test suite than to manually do the tests yourself. Sometimes it's even faster to run the test suite than to run your program. You can also set up your version control system so that whenever you commit, it first runs a battery of tests on the code and tells you if any fail.

I started doing tests with Django, which comes with a test runner script. Tests for models are useful because you don't have to set up all the views and templates etc. just to check if your model is set up correctly. Tests for form processing functions are also great because you don't have to keep manually entering the data into the form.

My form development workflow without tests:

  1. Make the HTML/template for the form interface and the class to handle the form
  2. Write the functions that validate the form data
  3. Open the page in my browser, fill out the form with invalid data, submit it.
  4. Examine the results printed on the screen to see if they're what I expect
  5. Find some mistakes and modify the functions. Go back to step 3.
  6. Do this again a couple of times to make sure all the combinations are validated properly (e.g. must enter ID number if "Student" is checked)
  7. Discover something that doesn't work, or need to change form because of client.
  8. Modify functions as necessary
  9. Go back and fill in the forms some more. Wish that Firefox autocomplete worked on more than one field at a time.

With tests:

  1. Make the class to handle the form. (Django has a Form base class for this purpose.) Set up all the fields.
  2. Write the validation functions
  3. Write several tests, each for a combination of valid or invalid input. e.g., One that submits a form with "Student" checked but no ID#, one with "Student" checked and ID# supplied but no "First Name", etc.
  4. Run the tests. Get a list of all my mistakes.
  5. Fix the mistakes.
  6. Repeat 4-5 until the test results say "Ok".
  7. Make the HTML representation for the form
Selene
+1  A: 

I'd recommend Kent Beck's book, Test Driven Development by Example. The code samples are in Java, which is syntactically similar to PHP. That aside, the book really is a good intro to TDD.

Grant Palin