I'm currently working my way through

It uses Rspec along with a TDD approach. I understand that writing lots of tests can help you ward off bugs as your app gets more complex, but I don't understand why you would write tests for simple things like the existence of a page title. It seems that you end up writing as many tests as actual code.

Is this better over the long term, or is it overkill?

+1  A: 

In my team, we wrote unit tests for everything but UI. There are some tools that can help testing UI, but we believe that the UI (like title) is too dynamic to test.

That's why when coding for the web, alot of people prefer the MVC design pattern, it creates a big seperation between ui and code behind, so it allows you to cover almost all of your code with unit tests.

And YES, We write a whole lot of unit tests!! It's the only REAL way to know your code really works!

+1  A: 

It seems that you end up writing as many tests as actual code

That is the idea.

+8  A: 

It seems that you end up writing as many tests as actual code.

Yes, and sometimes more tests than actual code. The benefit you seem to be missing is that testing for the little things will help you later when you change some seemingly non-related piece of code, and your old tests break.

Let's say that later on you decide to abstract your page title generation into a helper method that builds all of your page titles for you. If you already have the tests in place, then you'll know that your helper is working (or not).

+1  A: 

have a set of tests that always need to run even for setting the title is the idea behind TDD. That way you never push something to live and say "doh, the title isn't there anymore". You have a set of regression tests so that as you work on a project more and more, you don't end up breaking things down the road when you come back to a project after it has been untouched for a few months.

Additionally, when I write tests, it forces me to really think about the problem instead of just throwing code out there. I find that I need to refactor less when I am using TDD or BDD. The code that I just sit down and write because I am in a hurry ends up being sloppy. Perhaps it is just me, I need TDD to keep me on the straight and narrow road.

Geoff Lanotte
+3  A: 

It's called "Test Driven Design" for a reason

As valuable or perhaps more valuable than generating a regression suite, TDD is a design practice intended to generate the simplest thing that will work, objects with low coupling, and code with other desirable quality characteristics says it this way:

One view is the goal of TDD is specification and not validation. In other words, it’s one way to think through your design before your write your functional code.

You may find the entire article interesting, especially the Myths and Misconceptions.

And, yes, you will probably end up with similar numbers of lines of test code as you do product code. Practitioners believe that you do the work up front writing test or you do the work (and possibly a lot more) at the end, fixing bugs, when change is expensive.

+2  A: 

During the design cycle of a project, TDD forces you to think about your application's code interface before writing any code at all. This, in turn, forces you to think about the actual problem you're trying to solve, instead of just writing some code, as Geoff Lanotte mentioned.

Also, because unit tests are focused on a single unit, you're also likely to write classes that are focused and reusable, instead of classes that are coupled tightly.

During the development cycle, you only have to write the code that's necessary to make the unit tests pass. You don't have to worry much about design details anymore, allowing you to focus on the actual implementation.

During the life cycle of the product, it's easier to do maintenance work, because your test suite will quickly tell you if changes in the code have broken anything.

Niels van der Rest
+2  A: 

The basic principle at work in testing is that for any given piece of code with N branches, there are subsequently as many as 2N possible paths you can take through that code. That means for some method that might actually be pretty small, the number of ways it can be used can take a lot more to describe than the method itself.

Here's a common scenario:

if (@user.posts.count > @limit.posts)
  # Branch 1[:error] = "You have posted too many times today."
elsif (@user.suspended?)
  # Branch 2[:error] = "Your account is suspended. You cannot post."
  if (@post.special? and !@user.can.post_special?)
    # Branch 3
    flash[:warning] = "You cannot create special posts."
    @post.special = false
  # else
    # Branch 4
    # Not branching also has to be tested

  if (@user.recently_created?)
    # Branch 5
    @post.newbie = true
  # else
    # Branch 6

  # Branch 7

unless (flash[:error])!

Although this is pretty straightforward, creating the circumstances that will cause the logic to flow through the correct branches is not. You will probably have to write a test case for each specific one. This is not always trivial, but you can make it easier if you have a number of prepared fixtures or factory methods that construct things nearly ready to go.

Without testing each of these individual cases using automatic tests the chance of a malfunction due to an inadvertent change is significant. To ensure that it's working you will have to manually run through these paths, and it's often that you'll forget one and later this will cause trouble, probably the embarrassing variety.

The way to do less testing is to have lower code complexity by eliminating business logic that doesn't add business value, for instance. Keep only the most imperative things, discard anything superfluous.

If you're writing a thousand different tests for something that should be simple, maybe it's not simple enough in terms of design.

**The way to do less testing is to have lower code complexity by eliminating business logic that doesn't add business value, for instance. Keep only the most imperative things, discard anything superfluous.** And TDD does this for you. Seems to me a chicken and egg.
TDD doesn't do this for you, per-se, but it can expose where you've gone astray. If it's hard to test, it's probably hard to understand, and that's not a good design. TDD can help you uncover hidden complexity.