For one, I almost never just sit there and write unit tests. Unit tests are a means to an end, not an end in themselves. They are a way of answering "does this code do the basic task that it is supposed to."
For instance, some people will write a function, and then open an interactive session to test it out on a few values and make sure it's working:
def fact x
if x == 0
1
else
x * fact(x-1)
end
end
>> fact 10
=> 3628800
>> fact 7
=> 5040
But now you discover a bug:
>> fact -1
SystemStackError: stack level too deep
from (irb):2:in `fact'
from (irb):5:in `fact'
from (irb):10
So you fix it:
def fact x
if x < 0
raise "Can't take the factorial of a negative number"
elsif x == 0
1
else
x * fact(x-1)
end
end
>> fact -1
RuntimeError: Can't take the factorial of a negative number
from (irb):3:in `fact'
from (irb):10
But now you really ought to test to make sure it still works:
>> fact 10
=> 3628800
>> fact 7
=> 5040
As you can see, you keep on repeating the same tests... and you have to compare the results visually. Unit testing is a way of avoiding the repetition in this case; it reduces how much work you need to do. And while this is a silly little example, in the real world, it becomes more and more important, and more and more difficult to test manually. What this means, of course, is that people simply don't test the individual components; they just test the whole program. But then bugs crop up, and they're much harder to find. Or bugs happen, and they're fixed, but someone introduces the same bug all over again, because no one added a test case to make sure that didn't happen. Or someone looks at a big piece of code, and says "I have no idea what this is supposed to do, since it's not documented and has no tests... if I fix this bug, I have no idea if I'll break something else depending on it; maybe I'll just rewrite this from scratch."
Unit tests reduce all of the extra work in these cases. The best way to make them fun is to make sure that people understand all of the work that they are replacing, and the extra flexibility that comes from knowing what each piece of code is supposed to do. To some degree, people need to have a bit more experience with writing and maintaining a large code base to understand how important unit testing can be; if all of their code is something they write once and throw away, they'll never quite get it.
And unit tests shouldn't be written after the fact, as an extra chore once you have code that you "know" already works. Unit tests should be written first, or at the very least (since you sometimes forget to write them first) right after writing the code in question. This is called test-driven development, and it can help make your APIs better; if you write the tests that exercise the APIs first, you will learn where the APIs are a pain to use before you even write the code, and can redesign much more easily than if you only add the tests afterwards.
MbUnit
library has changed my life. Auto testing is important. Auto testing saves time. Auto testing saves money. Auto testing can save lives. Auto testing is the only way. Auto-testing is yet another safety net. When I am one of 50 people working on a huge architecture, I feel like yet another brick in a wall. With unit tests I am in control.