views:

1624

answers:

17

I'm a developer and have very much fun at nearly all stages of software developent, from gathering requirements, deciding on language, libraries and tools, sketching the architecture to implementing. Even bugfixing is fun. Unit testing is not.

Writing a unit test is not that bad, I can get over it. But the quantity of work involved in testing it right makes me shiver. I fear that I will spend much more time on testing than on actual implementation.

I've always managed to deliver working software without unit testing. But my current project is different from the ones before, and I know I finally have to get into it and write a whole lot of high quality tests. There are several points that make me not want to do it:

  • I don't know much about unit testing, but a short look into books like XUnit patterns made me realize that I'd need months or even years to learn enough about it to do it right. I'd like to spend those years learning about real developement skills.
  • We have a whole bunch of code with nearly no tests. The code is known to be buggy ans was never used in production, so we absolutely need tests. I think I could spend several months just writing tests and some more month to make them all pass before I can write my own code on top of it.
  • I'm coding something and realize Hey, I need a method Xyz(int abc) for that. I write this method in 30 seconds. If I now write all the tests needed for Xyz() then I spend 5 Minutes on writing tests. After that, I will have forgotten why I needed Xyz() in the first place. It just kicks me out of the zone.
  • Writing unit tests has linear complexity (e.g. x tests for each class / each method), but integrations tests seem to have quadratic complexity, as I'd need x * n * n tests for n interacting classes.

I've been programming for more than 16 years now, mostly as a hobby. If my future would be doomed to writing tests most of the time, this would make me want to change my profession.

So how do I overcome my aversion, get some motivation, and just do it? And do you think unit testing might ever feel natural to me, or maybe be fun in the end?

+5  A: 

Some of the aspects you mention are actually positives. For example, the fact that tests force you rethink dependencies and even to rethink why you need the object/method in the first place.

If you think "oh, and now I've got to write tests for this" then maybe you'll end up removing features you don't need simply to remove the need to test them. It's great for YAGNI.

I actually get a real kick out of writing tests. There is just something about knowing that you code has minimal/no dependencies and that there are rules that prevent the code being broken by another developer that makes me happy.

And remember, you don't have to write integration tests (I generally don't). At the end of the day, tests are about peace of mind for your code now and in the future (when you may not be the developer changing it).

I'd like to spend those years learning about real developement skills

I think you'll find that your skills are being improved as you start to write your classes with unit tests in mind.

We have a whole bunch of code with nearly no tests

Start by writing tests for a troublesome or complex method in an important class. You may find yourself refactoring the method in the process and thus making the code better. Unit tests for legacy systems is not an all-or-nothing deal.

Richard Szalay
A: 

Make it into a game by finding some way to mark your progress. Each test you write gives you points, each class or file that you cover completely with passing tests is a level, and you win when everything is covered and refactored and you get to write your own code. It's a little silly, but it might help with the monotony that can sometimes come with unit testing.

VirtuosiMedia
+32  A: 

"I'd need months or even years to learn enough about it to do it right."

This is not true. Therefore, your conclusion ("I'd like to spend those years learning about real developement skills") is false.

Indeed your implicit assumption ("unit testing is not real development") is also false, leading you to wrong conclusions.

Unit testing is one of the principle development skills. If it isn't testable, it isn't suitable for production use.

Does everything require 100% elegant, thorough test coverage? No.

Does everything need to be testable as a separate unit? Absolutely.

You can write some classes that are simple enough that a quick "sanity test" is all you need. The code is so simple and clear that extensive testing is redundant.

You will also encounter classes that require careful testing before you can finish coding them. This is a good thing -- you thought about the testable API first, wrote tests second, and wrote the code to that API last.

Doing it "right" takes almost no time. Design one test, then fit code to that test. You've done it right. That's all there is to it. Design for testability; writing tests is good practice but not a show-stopping all-hands-on-deck damn-the-torpedoes issue.

S.Lott
However, testing might be invaluable but it is no panacea. Just because code is testable and passes tests doesn't mean it's good code (see this here all the time). And code reviews still beat unit testing in terms of bugs found.
Joey
The point is not testing as ultimate or panacea solution. The question is -- incorrectly -- why *waste* time on testing. The important point not whether testing is sufficient, but whether testing is part of software development, and if so, how much effort to invest in it.
S.Lott
+8  A: 

I'd like to spend those years learning about real developement skills.

Unit testing is a valuable skill to have in your arsenal. Two reasons to chew over:

As you write tests and find numerous types of bugs, you will scrutinise certain areas a lot more. E.g. since I started unit testing, I now pay much more attention to loop termination conditions as that's where I've noticed a lot of bugs before. Same deal with edge cases / off by one errors. I still write the tests for them, but my code is less buggy after even before my first round of tests. Nothing makes you sit up and take notice like a failing test.

Just as importantly, testing can change the way you code. A few years back, I was fresh out of uni. I over-used inheritance and rubbish like the Simpleton pattern. Once I got heavily into unit testing, I was forced to think about structuring my code to make it more testable. Not only is my code now significantly more testable, but it is a lot better than it was! Unit testing has introduced me to patterns and principles that I would have otherwise been ignorant of.

To clarify: I am in no way saying that people who do not unit test will not know this stuff. What I am saying is that designing for testability will almost inevitably introduce you to new things as a side effect.

I attribute much of my progress in the last year to being exposed to unit testing!

Mark Simpson
Thanks for this perspective.
Liran Orevi
A: 

Here's a tip: release your code into production without having written unit tests for it, wait for it to break at the very worst possible moment and then feel the wrath and fury of a client who has to suffer the consequences of your buggy code. I'm not saying you should never release something to production without being fully tested, or that you should unit test every single line of code, but if you know your code needs to be bullet proof before going into production (you're not working for NASA, right?), then you also know that with that comes the extra effort needed to get it right.

Also, as others have said, I've also found that writing code with testing in mind significantly improves the quality of your design and helps when it comes to future requirement changes.

Alex Spurling
+5  A: 

Part of your apprehension about unit tests may stems from the fact that you don't seem to have realized any value from them yet. Perhaps you just need some additional practice first. Here's some power tips from my own experience to put you on the right track:

  • Strive for good (but not perfect) code coverage. This will tell you if you're writing unit tests to hit all the pain spots. But you don't need to test absolutely everything -- it's unlikely that a trivial getter or setter is going to go horribly awry.

  • Prioritize your unit testing. As you noted, it might be hard to write tests for a legacy code base that needs to be worked on. So don't do that all in one shot; instead, write a little bit at a time, focusing on the highest-risk areas first. You may even discover previously hidden longstanding problems.

  • Having to write tests is a mental check-and-balance system on the addition of new code. If you're forced to think about "how would I test this?" when you add a new method, you may often discover that there's either a better way to do what you want, or that you may not need it at all. Something that is difficult to unit test is often not written as well as it could be; when a unit test is easy to write, it means the functionality under test is relatively cohesive.

  • Don't have your unit tests do everything. Making exhaustive unit tests produces diminishing returns. Make sure that you're only testing stuff that your code does, and not results produced by underlying libraries. For example, if you write a method to draw a circle on the screen, don't double-check that the results of the sine and cosine operations.

So how do I overcome my aversion, get some motivation, and just do it?

You already describe the enjoyment you get from programming, and how this comes from being "in the zone". I think unit tests will feel start to feel more natural to you when you start noticing that they catch errors you'd otherwise have missed. You may look forward to being able to work faster, now that there is a buffer that can catch some of the problems. But after some practice, I suspect that you'll start to feel that unit tests are just as much a part of the zone as development itself.

John Feminella
+1  A: 

Even bugfixing is fun.

In all the time I've been a programmer (about twice as long as you) I have never, ever found bugfixing to be fun. Having a product work correctly the first time it's deployed, meeting all the client's requirements and responding quickly to changes in those requirements - now that's fun! And writing unit tests allows me to do that.

If you're serious about what you said regarding bugfixing, then maybe you're right - unit tests may not be for you. If you actually enjoy the most painful bits of the software development cycle, then you are not going to enjoy implementing practices that remove the pain.

anon
After you implement something incorrectly and a test fails, making the test pass qualifies as bug-fixing. So he'd still get to bug-fix, it'd just be... not nearly as painful :)
Mark Simpson
True - so maybe he wouldn't enjoy it so much? People are strange...
anon
He just likes troubleshooting, it's like solving a puzzle. I find it fun too but I do write unit-tests.
zvolkov
I can understand liking puzzle solving - I do the occasional sodoku. What I can't understand is enjoying solving puzzles that I myself have created!
anon
Fixing a bug gives you the satisfaction that you won't see that problem again and you'll know to look out for it next time. I think if you manage to get something right first time and it goes into production it always gives you that nagging feeling there might be something you missed.
Alex Spurling
I think exactly the oposite - if I fix a bug, I think "did I really fix it, under all circumstances?" Of course, given the right tests (written at the same time and in response to the bug fix) I can be fairly sure that I did.
anon
Why people talk like Unit Test produces bug-free software? And I think finding bug fixing fun is personal choice. Sometimes I agree sometimes I hate, depends on the bug :)
dr. evil
A: 
  • Try writing as many tests as possible before you write the code under tests. This way it won't seem as redundant as when you write them after the fact.
  • Understand that unit-testing is not as much about testing as much as making sure that 1) the code is loosely coupled and 2) the code will keep working in the future, e.g. it's a way to make it easy to change your code in the future.
  • perhaps you're also a kind of person who obsesses about being thorough but the level of effort it requires frustrates you? Then just take it easy and do the minimum that your guts allow you to, it's still better then no unit-tests at all. Gradually you'll get the feel for how much is just right.
  • finally, if systems you create are so simple that you can test them by launching entire system and interacting with it live or you write your software purely for fun, and you never change the code once you wrote it, and you have unlimited time to troubleshoot bugs then you really don't need the freaking tests!
zvolkov
+3  A: 

But the quantity of work involed in testing it right makes me shiver.

Does this imply that code is too coupled to test independently , think of TDD as design tool rather than testing tool , you mentioned that you are fond of sketching the architecteure why not use Tests to drive your design . I never consider code coverage as goal of TDD ..its a nice little side effect of well designed code ( using TDD ) .

Surya
+9  A: 
RobS
+1 because, while unit-tests have been helpful for myself and company I work for, there is also plenty of resources on the Internet presenting them as a silver bullet.
javashlook
TDD != testing , tdd == design ..its an unfortunate name IMO
Surya
My point is that it can be an expensive (effort/upkeep) design approach
RobS
+1  A: 

You should be able to get some quick wins going on unit testing without totally changing around what you're doing. You don't have to understand mocking frameworks and Dependency injection before you start. So I don't think it will "take months" to get any benefit.

Find library functions and utility classes to test, they are already fairly isolated. For some complex functions that aren't close to the surface, I found it to be more interactive and more fun to get the functionality of the code right by calling it from the test harness rather than running the whole program - it was faster than running the program, and I could generate the whole range of inputs rather than waiting for them to crop up. Repeating the tests gave me confidence that by fixing or extending one case I wasn't breaking another. That feeling wasn't tedious at all.

Anthony
+17  A: 

Here's my take on the issue. I'll try to respond to your statements in order you presented them.

Even bugfixing is fun. Unit testing is not.

Here I have to flat-out disagree. While bug detection and debugging might be challenging, there's almost always some kind of pressure involved. Especially for a system already in production. Plus, you never can tell how much time you will actually need. You may get that sucker in a few minutes, but sometimes you'll be stuck for several days. Where's the fun in that?

Writing a unit test is not that bad, I can get over it. But the quantity of work involed in testing it right makes me shiver. I fear that I will spend much more time on testing than on actual implementation.

I have found out that for even moderately complex systems, unit testing is an integral part of actual implementation. How can you know, with at least some degree on certainty, that your code works as intended without trying it out? Unit-testing is just about approaching that trying-out part systematically, one part by one, in isolation.

More importantly, what about you, or for that matter, one of your colleagues, having to modify and/or improve that code some time down the road. Will you be able to remember all the intricacies involved? How will you make sure you're not introducing new bugs while fixing the old ones or adding a new feature?

I don't know much about unit testing, but a short look into books like XUnit patterns made me realize that I'd need months or even years to learn enough about it to do it right. I'd like to spend those years learning about real developement skills.

Like others said, testing is a crucial development skill. Also, what other development skills do have on mind? How would you verify you are using them properly?

Then again, every development technique takes time, first to learn, and than to master. It's the same with testing.

We have a whole bunch of code with nearly no tests. The code is known to be buggy ans was never used in production, so we absolutely need tests. I think I could spend several months just writing tests and some more month to make them all pass before I can write my own code on top of it.

What's the alternative here? Spend a few months writing code without tests, and then just push it into production. When Q&A, supposing you have them, starts piling up bug reports, how would you go about fixing each and every one of them?

I'm coding something and realize Hey, I need a method Xyz(int abc) for that. I write this method in 30 seconds. If I now write all the tests needed for Xyz() then I spend 5 Minutes on writing tests. After that, I will have forgotten why I needed Xyz() in the first place. It just kicks me out of the zone.

Quite frankly, this sounds like a lame excuse. First of all, you are most certainly allowed to write a larger block of code, use the Xyz() method or whatever, and then write the tests for Xyz(). But, you're forgetting that with that 5 minutes spent on tests, you are actually investing the time now, instead of spending it on debugging later.

Tests also serve as an example of using your code for other members of your team. Unlike javadocs, they have to be runnable, and of, course, will complain if you change any part of your API or program logic, which may seem like an extra job from your personal point of view, but, what about other people depending on that code?

Writing unit tests has linear complexity (e.g. x tests for each class / each method), but integrations tests seem to have quadratic complexity, as I'd need x * n * n tests for n interacting classes.

There is only so much you can do with integration tests. Don't become obsessed with 100% code coverage, and of course, you are not required to replace the Q&A department with your tests. Unit test should check the behavior on classes in isolation, and once you're certain about it, you don't have to test all of that in combination with some other class.

For instance, if you're implementing a new kind of data structure, you would write tests against hard-coded data, checking the corner cases especially. But, you would not be writing integration tests concerning that structure from every placed that it is being used from.

On the other hand, if there are that much distinct cases regarding integration of your classes, then it may very well be you're dealing with an inherently complex problem. But let me ask you this, would you rather be redeploying your application again and again, stepping one statement at a time in the debugger, or would you write code representing common cases and run those specific cases in matter of seconds? Without the UI, without the need to enter the data again and again? And with the added benefit of that testing code being available at any point in time later on.

I've been programming for more than 16 years now, mostly as a hobby. If my future would be doomed to writing tests most of the time, this would make me want to change my profession.

Don't get me wrong, but maybe you don't have enough professional experience regarding the delivery of a software product. The truth is, I originally saw little point in testing, and it was only after numerous bouts with debugger that I thought that there must be a better way. Long start-up times of J2EE servers also "helped" in me accepting the unit-testing as a methodology, since it felt much better to think about the code for a few minutes more, and write the reflection of what code is supposed to do in form of a test, then wait for 2-3 minutes for server to redeploy on each bug fix, no matter how small.

My opinion is that if you want to turn software into profession, you have to invest time into unit-testing. It is not a silver bullet and learning how to write testable code, what to test etc. takes time and patience. But, so does every activity you intend to do properly, right?

javashlook
Sums it up pretty nicely.
lothar
I only disagree with one thing: Bugfixing (or better: bug *finding*) is fun! Especially if it takes days rather than minutes. Nothing is as challenging as a good challenge...
Treb
@Treb - actually, "nothing" is not a challenge at all ;)
Dominic Rodger
You lost me when you disagreed flat-out with a personnal statement of taste, near the start.
MrZombie
@Treb: While bug finding may be fun for you, our responsibility is delivering value to the customer. Spending five minutes writing a test to get the code right in the first place is more cost-effective than spending days debugging later.
TrueWill
@TrueWill: I didn't say *don't write unit tests, because hunting down a bug is more fun* now, did I?
Treb
+11  A: 

Compromise

There are people out there which follows Unit Testing as a religion, which I respect but it's definitely not my style.

  • Unit Testing is easier then it looks, you can learn it enough in 3 days. Know the basics, get the right tools and that's enough. You don't have to know how to mock a TCP communication. Use functional tests for this kind of tasks, don't spend your time to implement Dependency Injection for everything (at least not for the sake of Unit Test it). Also take a look at TypeMock (if you are .NET guy). It's great for writing Unit Tests for a legacy app.
  • Write Unit Testing for only Public Methods and only for tricky private functions, as soon as overall class behaves correctly internals doesn't matter. Only downside of this if a bug hits, it might take longer to spot where it's.
  • Always write a unit test for every identified bug
  • It saves your ass and when you feel you get addicted to it, because you know how it feels to spend ages because you broke some other thing and forget about it.
dr. evil
Very good answer. Software testing is no religion but a pragmatic solution.
frast
+2  A: 

I'm strongly in the "TDD improves implementation" camp. I used to be in the "What am I doing writing all these tests that just reiterate my implementation" camp.

Our dev team decided to make unit testing an integral part of our coding lifecycle, and when we started on a new project it was a bit rough at first to design our core frameworks to be testable. After quite a bit of redesign work, we ended up with FAR better class factoring than when we started, directly as a result of writing the tests being too "hard".

That's when I really "got" it, and it vastly improved my design skills to wrap my head around the level of isolation required for clear and easy testing. The time spent on that has been repaid several times over, now for a new project it is just second nature to think in that way and design the tests along with the implementation.

As I go back to some other things I wrote before my conversion, or as we pull some legacy classes into the new project, I can easily see where the design would have been simpler and more elegant with testability in mind.

As for aiming for 100% coverage, I'm not so sure that the time spent writing a test for a constructor that only assigns parameters to fields is all that well spent. Then again, it really is only about 20 seconds.

I also definitely agree about writing tests for any defects found, the other day during a code review I could see two cases where a class would fail. So I just wrote the test cases, checked in the tests that would fail, and warned the dev team their build was about to fail. :)

Darren Clark
A: 

Hi There, i have a little question for you. Wouldn't it be easier to develop tests when you write the spec in an excel spreadsheet to make it a little walkthrough?? and then start coding as it would mostly help you reduce the frustration as well.

Andy
+2  A: 

Don't try to do everything at once. Unit testing and TDD doesn't have to be a religion. Picking and choosing what aspects of it you employ is perfectly OK and will yield partial benefits. You can expand it when you feel more comfortable.

Go for the low-hangig fruit. Look for parts of the application that are relatively easy to isolate and test. Prime candidates are independant components with a narrow API (the most simple case: static methods) - this can be a good learning experience.

Write high-value tests first. The most valuable tests are for code that has a high potential for errors and/or changes frequently. Even better are tests that can be parameterized and/or applied to lots of code at once, for example a test that checks configuration files for syntax errors.

Michael Borgwardt
+3  A: 

My background is somewhat similar, having been mostly a hobbyist programmer for a long time. For the last four years I've been involved with unit testing at work. I've got some theoretical background about testing, too, but most of the learning has happened doing, as with most things.

I strongly advise you against spending N months for writing tests for existing code because that if anything will make you hate unit tests forever. Instead, take small steps and learn as you go. Getting any victories by writing the tests, however small at first, will keep your unit test motivation up. It also helps when you notice your skills getting gradually improved in the process.

It's a bit difficult to tell what to do in this case without detailed knowledge about the development tools and platforms. I can suggest some general guidelines, however. After ensuring that your developement tools are in a good shape to run any test with a single click (or two), try the following:

  1. When you need to fix a bug in the application's business logic, by choice in some part that is already as isolated as possible (e.g. some mathematical, date handling or string manipulation function), first write a test that exposes the bug, and nothing else. Then actually fix the bug and watch the test pass. This should be easy to start with, and you'll probably also notice having dealt with the problem faster than by launching the full app and trying it out through the user interface.
  2. After mastering unit testing for isolated cases, you'll eventually need to fix or implement something that can't easily be unit tested without modifications to the existing code. This could be a method that depends too heavily on some hard-to-generate user input, for instance. But refactoring the code to enable unit testin is usually pretty straightforward. Afterwards writing the test is a breeze, and as an additional bonus you notice that the original code better to read and maintainan.
  3. Now you probably have established a good test coverage for a number of the the new bugfixes and features. If the app's architecture is not that perfect, as it usually happens, you'll probably find or be told about new bugs in some areas supposedly unaffected by the previous modifications. Note that you haven't written any tests for the untouched code yet! By now, however, it should be clear that you need to write a test to detect the new bugs and fix them, making the tests to pass.

And so on. Notice that so far the only tests written were to help making the new code modifications work, as per test driven development. Writing the tests should only happen when there is motivation (i.e. business value) involved.

To sum it up:

  • Write the easy tests first and test as small and independent units as possible. When later writing the integration tests, you only need to worry about how the individual components are glued together.
  • Write tests only when modifying existing code, in a test-driven fashion. This way you get immediate value from writing the tests, and get the test coverage improved as a positive byproduct.
  • Write only tests that matter, don't even think about trying to cover every possible combination. Later you might want to take some code coverage tools into use to watch for the branch coverage of tests in the most important areas.
Eemeli Kantola