views:

884

answers:

8

TDD is something that seems to be on everybody's lips these days, and I have tried some on my own but I don't think I'm getting the idea. I am getting a grip on how to write a unit test, but I don't understand exactly what my unit tests should test.

  1. If I have an action method that returns a list of data, what should I verify? Only that the view name is correct, or should I verify the data as well?
  2. If I should test the data as well, won't I be writing the same code twice? What is the use of testing the data, if I use the same method to retrieve the data I'm comparing to?
  3. Should I test the methods adding/editing my data too? How do I verify that a record has been added/edited/removed, in a correct way?

I know it's quite a lot of large questions, but I haven't become any wiser from reading articles on the internet, as they all seem to be concerned with how to test, and not with what.

As an example - I have (or, am going to write) a GuestbookController, with methods for viewing, adding, editing and removing posts. What do I need to test? How do I do it?

A: 
  1. For a given input or state, you should test that the return value of the method is as you would expect. If your method returns lots of types of data, you should verify they are all correct.
  2. Use a small set of sample data right in your test case. Don't load anything from disk or a database. Pass in a string or construct the test object there in your test. Given that small set of data you expect to have a very specific result from your method, which you compare to your actual result. You want to avoid much code that computes values for you in your tests, because then you would have to write tests for your tests to makes sure they work properly!
  3. Test every bit of code that you write. For adding a record (if your tests are hooked up to a test database) you can simply query for the last inserted record, or compare the total numbver of records before and after and make sure it incremented by one. If you have a mocking/stubbing framework you can skip the database and assert that the method that saves things to the database was called. To test an edit just retrieve the edited record form the database again and assert that the value has been changed from its old value. Or if mocking/stubbing, that the method to change the attribute value was correctly called.

But really, write a test for bit of code that you are about to write. Watch it fail. Then write just enough code to make it pass. Now write another test and repeat.

Squeegy
A: 

I think you can gain most benefit from Unit Test by Test Driven Development / Test First Development. You should first write test then write the code that pass the test. And What should you test first?

I find it really useful to write tests from user stories. Most of the time I write Top-Down style tests from starting user stories.For example our user story containt such a task:

  1. When User Save an Order View Should Display Information Message.

I usually Write test for this story from presentation/controller layer

  [Test]
    public void When_User_Save_Order_View_Should_Display_Information_Message()
    {
        using (mockRepository.Record())
        {
            repository.Save(order);
            view.Message= "Order saved successfully";
        }

        using (mockRepository.Playback())
        {
            presenter = new OrderSavePresenter(view, orderRepository);
            presenter.Save();
        }
    }

And then I continue to write test for each classes that I mocked or needed.

mcaaltuntas
+1  A: 

I don't bother testing simple things, like a getter or a setter. You don't need to test the compiler itself, so checking that a value gets assigned when you call a setter isn't useful.

In general I try to write a unit test for each method of a class that has actual code. This way if someone ever breaks a method later on, it will be caught.

For example, someone changed a method like "addElements(String[] elements)". They tried to use "for(int i=0;i<=elements.length;i++)". An out of bounds exception was thrown when the unit tests ran after check-in and it was fixed.

For something like a GuestBookController, there may be methods you can test offline, but you'll probably need to actually set up a connection to a testing database server. From there, have a test to add a post, edit a post, and remove a post, verifying at each one the change occurred using a method that retrieves the post.

Unit testing should make you feel more confident that your code works, and that when you make a change it still works. Add unit testing code until you feel confident that it is adequately tested. In the above example, you don't have to worry as much about accidentally breaking the guest book. When you make a change to the code, the unit test confirms that you can still add, remove, edit, and retrieve posts.

Stephen Pape
For *unit testing* the GuestBookController, the controller should really use mock DAOs and just assert the correct interaction has taken place. A unit test should never hit the database, that's the integration tests job
MrWiggles
+13  A: 

Unit Testing (UT) != Test Driven Design (TDD)

This confusion seems to be fairly common. UT is all about code coverage. TDD is concerned with features. They are not the same thing [sorry Joel!]

With UT, you write whatever code you want to, then go back and test every single function (even some of the trivial ones).

With TDD, you select the next feature and write the test for that feature first. Write only the test for that feature, and test coverage is irrelevant. You write the test first to force interface decisions to be made up front. Then you write the code to pass the test (bearing in mind the 'simplest thing that can possibly work'). Then you refactor the code based on what you've learned. Then you go on to the next feature (presumably after check-in and re-running all unit tests).

If desired, develop using TDD then go back and complete coverage with UT tools. If you're creating a class library or other API for developers to use, the more test coverage the better ;-)

If you're just writing an app to do five specific things, TDD alone should be sufficient.

Steven A. Lowe
I'll agree that UT != TDD, as TDD is a methodology, which uses UT. However, I'm not aware of any definitions in TDD literature, that support your claim about UT being all about coverage. Please elaborate.
Brian Rasmussen
@[Brian Rasmussen]: the UT emphasis on code coverage comes from two sources - vendors of code-coverage tools, and people distracted by (fascinated with) code-coverage metrics. UT itself is a tool, not a methodology. There is no mention of code coverage at all in any of the TDD literature I've read.
Steven A. Lowe
With unit testing, you do not test every single function. For example, you do not test getters and setters that have no logic in them. However, if they do more than return or set a value, then they are indeed tested.
Thomas Owens
+3  A: 

Test the contract of the module's interface you are testing:

  • If a client would expect a specific behavior when using your class, test it.
  • If your class should prevent some behavior from the client, as defined in its contract, test it.

By client I mean the code that use your class.
By expected behavior I mean the expected result of a method on return values and object states.

And focus your tests on logic (if, for, while, etc.), because flat stuff like properties have smaller chances of failing without being caught by normal uses of your application.

total
+1  A: 

These are generic guidelines I find useful for unit testing:

1) Identify Boundary Objects (Win/WebForms, CustomControls etc).

2) Identify Control Objects (Business layer objects)

3) Make sure to Write Unit tests at least for control objects public methods invoked by boundary objects.

This way you'll be sure you're covering main functional aspects (features) of your app and you don't run the risk of micro-testing (unless you want).

JohnIdol
A: 

In TDD you write specifications of the systems behaviour and use them to drive the design of the system. You write one test for one tiny behaviour, then watch the test to fail, and then write code to pass the test. At all times you keep the code quality as high as possible by refactoring regularly, so that making more changes is easier.

Examples of how to do TDD: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata

Also see my answer from http://stackoverflow.com/questions/507000/writing-standards-for-unit-testing/510996#510996 - it has examples and links for more information. Here are also good links to follow: http://code.google.com/p/instinct/wiki/UsersGuide

Esko Luontola
I'm so tired of all the dogmatic hand-waving of the real-world examples. In your first link no one ever provided a satisfactory answer to Keith Gregory's example. The fact is that there are engineering concerns we have for our implementation that are not supposed to be a concern of the public API.
Jeremy
+2  A: 

I think there's a bit of Shu-Ha-Ri here. You're asking a question that is hard to explain. It is only after practicing and struggling to apply TDD that you'll get the What. Until then we'll give you answers that don't make sense, telling you stuff in the spirt of Monads Are Burritos. That won't help you and we'll sound like idiots (monads are clearly lemon-chiffon pie).

I'd recommend getting Kent Beck's TDD book and working through it, and then just practicing. "There's no royal road to Ri."

Jeffrey Fredrick