Poll: Do you test your code?

611 points by petenixey ↗ HN
Do you have tests that run every time you push and ensure that the functionality on your site works?

There's always a lot of debate around testing and I'm interested to see how much people do and how satisfied they are with it

IF YOU'D LIKE TO ENCOURAGE OTHERS TO ANSWER, PLEASE UPVOTE - TY

350 comments

[ 2.1 ms ] story [ 268 ms ] thread
We have a test suite covering most of the code. We'd like to do more testing are doinh it.
(comment deleted)
In my experience, on projects with often-run automated unit test suites with good coverage, development goes faster. Part of this might be because for code to be highly testable, it usually also has to be well-designed and architecturally sound.
I agree. When interviewing I can usually weed out those who write tests (and write good tests) from those who just claim they do.

How?

People who don't really write tests will tell me that the advantage of unit testing is being able to see when code changes have broken stuff (which is fair enough and true).

Those who regularly write unit tests will probably bring this up- but often their first point will be 'It helps to structure code properly, make me think about dependencies, modularise code appropriately'

You are mixing up automated Unit Testing with TDD. They overlap a lot but they are not the same.

There are people who could write quality software with good test coverage without following TDD style.

Never forget you write software, not tests. Tests are here to increase quality, they have no raison d'être by themselves.
tests are a lot more about design and refactoring than they are about quality.
what

what are you even saying

i dont even

(

More concretely, if you use testing to drive your refactors and architecture--as opposed to, say, finding pain points in normal code or actual design time in preproduction--I would be concerned that you are "guardrail programming", as a gentleman put in a talk I saw recently.

When we drive, we don't have guardrails to bounce us back on the road every time we veer off--they're there to protect us against accidents or when something goes seriously wrong with our vehicle. If you told somebody that you drove from city A to city B by hugging the guardrail, they'd say you were nuts.

Similarly, depending on unit tests to do design is strange--they're there to be sure that your code functions according to contract.

)

I disagree completely, and your comment makes me think you've never seriously used unit testing.

Writing tests makes you think about how pieces of your code interact with each other, dependencies etc.

As an example, if you're trying to test Function A and are finding you need tens of lines of setup code to be able to do so, then that would be a warning sign that you may want to think about refactoring out some of those dependencies

I've seen code in productions apps with comments along the lines of "this isn't optimal, but it's easier to test". Every time I do, I die a little inside.

Unit tests are THE most overrated buzzword of the last 10 years.

If you mean that unit tests have accumulated a lot of dogma over the past few years, and you are saying they are "overrated" because you still need to think about how, what, and why you are testing, I agree.

If you are using your post as an excuse for not using automated testing at all, I completely disagree. That's the bad kind of developer laziness.

On the other hand, I do have to concede that when competing against people who don't use unit testing on the open market, I come off looking like a wizard in terms of what I can accomplish in a reasonable period of time and the sorts of things I can do (successful major changes to large existing code bases you wouldn't even dream of starting), so maybe I shouldn't try so hard to encourage others to use them sensibly. So, I mean, yeah, totally overrated. Have I also mentioned how overrated syntax highlighting is? You should totally just shut it off. Also, fixing compiler warnings are for wusses, and what moron keeps putting -Werr in compilers?

How much of that complexity is self-inflicted? Most of the unit testing advocates I know are also the worst architecture astronauts.

Every line in a codebase has a cost, including tests. I'd rather deal with a code base that's as trim as possible.

I've done unit tests before, but I don't find that they help that much, because they don't solve the most common source of actual production issues: things you didn't think of.

I find they do help there. Having unit tests makes me trust my code better. Confronted with a "it does not behave as I would expect" issue, that trust helps me focus attention away from the implementation of those functions.

Problem with that is that, to get that trust, I need to know that unit tests exist, and, preferably have spent time writing or reading them. Question then is whether that time would not be spent better on reading the existing code. I think that, often, the answer to that is "no", but I cannot really argue that.

Perhaps, it is because writing unit tests puts you explicitly in "break this code (that may not have been written) mode". Writing a unit test that calls a function with some invalid arguments and verifies that it throws is often simpler than reading the code to verify that. Also, unit tests may help in the presence of bug foxes and/or changing requirements. Bug report/Requirements change => code change => unit tests break => free reminder "oops, if we change that, feature X will break".

How do you then know that everything works fine when you do large scale refactoring? Test everything manually? (genuine question, not trying to be snarky).
He doesn't. And I'm not being snarky either. People will say they do, but they don't have any assurance of it. And furthermore, over time they'll learn to stop making these sorts of changes because they don't work, become very cynical about what can be done, and internalize the limitations of not using testing as the limitations of programming itself.

And then these people will be very surprised when I pull off a fairly large-scale invasive refactoring successfully, and deliver product no engineer thought possible.

I'm not hypothesizing; this has been my career path over the past five years, and I have names and faces of the cynical people I'm referring too. You can not do the things I do without testing support. I know you can't, because multiple people who have more raw intelligence than I try and fail.

It is equally true you can't be blind about dogma, 100% coverage being a particularly common bugaboo, but I completely reject the idea that the correct amount of automated testing is zero for any non-trivial project.

I'm curious as to what exactly you mean. Can you give some examples? If your're frequently making large-scale changes, I'd spend more time worrying about why you're having such a hard time nailing the requirements down.
A framework previously designed to work on a single device was ripped apart and several key elements were made to run over a network remotely instead. (That may sound trivial in a sentence, but if anyone ever asks you to do this, you should be very concerned.) The framework was never designed to do this (in fact I dignify it with the term "framework"), and tight coupling and global variables were used throughout. This was not a multi-10-million line behemoth, but it was the result of at least a good man-century of work.

As mentioned in my other post, first I had to bring it under test as is, then de-globalize a lot of things, then run the various bits across the network. Also testing the network application. Also, by the way, releases were still being made and many (though not all) of the intermediate stages needed to still be functional as single devices, and also we desire the system to be as reverse-compatible as possible across versions now spanning over a year of releases. (You do not want to be manually testing that your network server is still compatible with ~15 previous versions of the client.) And there's still many other cases I'm not even going into here where testing was critical.

The task I'm currently working on is taking a configuration API that has ~20,000 existing references to it that is currently effectively in "immediate mode" (changes occur instantly) and turning into something that can be managed transactionally (along with a set of other features) without having to individually audit each of those 20,000 references. Again, I had to start by putting the original code under a microscope, testing it (including bug-for-bug compatibility), then incrementally working out the features I needed and testing them as I go. The new code needs to be as behavior-similar as possible, because history has shown small deviations cause a fine spray of subtle bugs that are really difficult to catch in QA.

I could not do this without automated testing. (Perhaps somebody else could who is way smarter, but I have my doubts.) The tests have already caught so many things. Also, my first approach turned out wrong so I had to take another, but was fortunately able to carry the tests over, because the tests were testing behavior and not implementation. (Also it was the act of writing those tests that revealed the performance issues before the code shipped.)

This isn't a matter of large-scale requirement changes on a given project. This is a matter of wanting to take an existing code base and add new features that nobody thought of when the foundation of the code was being laid down 5-7 years ago. (In fact, had they tried to put this stuff in at the time it would have all turned out to be a YAGNI violation and would have been wrong anyhow.) Also, per your comment in another close-by thread, the foundation was all laid down prior to my employment... not that that would have changed anything.

The assumption that large-scale changes could only come from changing requirements is sort of what I was getting at when I was talking about how the limitations of not-using-testing can end up internalized as the limitations of programming itself.

Might I also just say one more time that testing can indeed be used very stupidly, and tests with net negative value can be very easily written. I understand where some opposition can come from, and I mean that perfectly straight. It is a skill that must be learned, and I am still learning. (For example: Code duplication in tests is just as evil as it is in real code. One recurring pattern I have for testing is a huge pile of data at the top, and a smaller loop at the bottom that drives the test. For instance, testing your user permissions system this way is great; you lay out what your users are, what the queries are, and what the result should be in a big data structure, then just loop through and assert they are equal. Do not type the entire thing out manually.) But it is so worth it.

So, at the end of the day, you never actually did design-from-scratch work, and instead used tests to verify incremental design improvements (key part: verify not create)?
New hacker news rule: if you haven't done at least 80% of what he's talking about, you can't dick-measuring-contest him.

From scratch work is the easier part of programming.

I've done this before, friend. Starting from scratch is indeed easier.

The point I was making was that he used unit tests to confirm his design (as a safety net) and not as a primary design tool.

Starting from scratch does not take into account all the growing pains the previous software hat that made it into the quagmire you have learned to hate.
The sum total of the improvements were not incremental. Testing helped give me a more incremental path, but from the outside you would not have perceived them as such.
That's an amazing story.

Few questions:

1) How many lines of code is in that man-century project? Is the number of lines of code ~proportional to the number of man hours, or lines(man-hours) function is ~ logarithmic?

2) How does your typical project look like (or how does that project look like) in terms of testing vs coding? Do you spend few months of covering old code by tests and only then start testing? Or you do "add tests - add features - add tests - add features - ..." cycle?

What's the proportion between time spent on writing tests and writing code?

3) What's the proportion of time you spend directly working (analyzing requirements/testing/writing code) and generally learning (books, HN, etc.)?

4) Do you do most of the work yourself or you mostly leading your team?

5) How do you pick your projects, and when you pick them - what are your relationships with the clients: Fixed contract? Hourly contract? Employment?

Thanks!

If you've only worked on projects with nailed-down requirements, you're probably not working on the sorts of projects most HN people face. The requirements change because the world changes, or our understanding of it. That's the nature of the startup. Stable codebases serving stable needs don't need as much refactoring, that's true. And in those cases units might be a waste of time. But for those of us (the majority, I'd wager, at least around here) who work on fast-moving, highly speculative projects, they are an absolute godsend.
I get the impression that the code base on which you pulled off the "large-scale invasive refactoring" was not initially under test, else why would the cynical engineers think it could not be done. So did you have to bring the legacy code under test first?
I don't do large-scale refactoring. Seriously. Small pieces? Sure.

But I've never, in 15 years of development, had to rewrite half of an application I've already written.

Spending a large amount of extra time and energy, things I don't have an excess of to begin with, for a "might" or a "maybe" seems like a rather poor choice to me.

I agree 100% ;) I'd have to start learning new stuff that i don't necessarily want to get into yet.!
Aside from the unit tests pb, suboptimal but easy to test code is critical in a lot of situations, like time critical bug fixes or last minute feature addition on a production site.

Most of the time, testing takes more time than writing the code, so throwing optimality under the bus can be the best choice. If it's Good Enough nobody's going to rewrite, but I wouldn't see it as something inherently negative or shameful, it's just a question of priorities.

My original disagreement was more along the lines of "unit testing is more important for design than for QA" and less "unit testing is important".

I certainly support unit testing, as its essential--and anyone telling you otherwise is bonkers--to ensuring that code follows contract.

That said, if unit testing was great for design but didn't spot errors, it'd be useless. Whereas, if it was useless for design and good for errors, that's okay, because I can do the design work myself.

"Writing tests makes you think". A developer should already be thinking about these things when they are writing their code.
Sure, but context shapes behavior. People should be eating better too, but that's a lot easier to do when you have a fridge full of vegetables than a cupboard full of Doritos. Test-driven development forces me to think about code from the outside first.
On the other hand, if your code is full of architectural compromises, special cases and privilege escalation tricks just to allow you to test everything in some particular way, maybe the tail is wagging the dog?

There are many ways we try to improve code quality and make sure we get it right. Automated test suites are only one of them. Software design needs to take multiple factors into account, and letting one of them arbitrarily dominate all others is a dangerous path to take.

If I have a function/module/method buried deeply inside my system such that testing it requires either ten lines of setup code or backdoors ("special cases and privilege escalation tricks") in the deployed code, that might say something interesting about my architecture in either case. Is the code really only ever going to be called from that one place and in that one way, and if so, exactly how valuable is it? Sure, it might be that the only place I currently want to call (say) a weighted modulo 11 checksum is in credit card validation and the context there is I have a third-party payment gateway and a valid order object and all that stuff, but I would still be looking at surfacing the actual calculation in a library module somewhere that I can test it without doing all this setup. I grant you that architecture is only ever easy in retrospect - that's why we refactor - but I don't think that represents an architectural compromise.
If all your algorithms are as trivial as calculating a weighted modulo 11 checksum, then the sort of case I'm thinking of doesn't apply. However, in real code, we sometimes have to model situations and solve problems that are inherently complex. The algorithms and data structures we work with will necessarily reflect that essential complexity, and ultimately so will our code.

Beyond a certain point, I think automated tests that give simple yes/no answers are no longer a particularly effective way to test certain types of complex algorithm. Sometimes there are just too many possible inputs and interactions between different effects to get a sensible level of coverage and draw any useful conclusions from that kind of testing alone. You might still have some automated tests, but they are more like integration tests than unit tests at that point.

Internally, you could try writing almost-unit-tests for the implementation details, but then you get into the usual concerns about backdoor access and tying tests too closely to implementation details that might change frequently. Alternatively, some form of careful algorithm design with systematic formal proof might be called for. Maybe instrumenting the code and checking the actual values at key points will highlight errors that aren't yet manifesting as faults, things that a boolean automated test would miss because they haven't violated some arbitrary threshold but which form an unexpected pattern to a knowledgable human observer. However, in these cases, you really want the code to be as simple as possible, and hooks to permit internal access to run some automated test cases as well could cause an awful lot of clutter.

> If all your algorithms are as trivial as calculating a weighted modulo 11 checksum, then the sort of case I'm thinking of doesn't apply.

My estimate is that 98% of all programming everywhere is as algorithmically trivial as calculating a weighted modulo 11 checksum - probably more so - and it acquires its bugginess from accidental complexity due to poor factoring, and from conflicts at interfaces. Test-driven development is pretty good, in my experience, at helping ameliorate both these problems.

Of course, that doesn't mean I actually do it 100% or even 80% of the time. I'm happy to agree that it's no panacea: testing threads and UIs are particular pain points for me, and usually I substitute with either Thinking Really Hard or just Not Changing Stuff As Much

Formal proof for me is stuff I learnt at college, forgot subsequently, and keep meaning to reread up on. Thank you for prompting it back up my TODO list

> My estimate is that 98% of all programming everywhere is as algorithmically trivial as calculating a weighted modulo 11 checksum - probably more so - and it acquires its bugginess from accidental complexity due to poor factoring, and from conflicts at interfaces.

I think it depends a lot on your field.

If you're working in a field that is mostly databases and UI code, with a typical schema and most user interaction done via forms and maybe the occasional dashboard-type graphic, then 98% might even be conservative.

On the other hand, if you're doing some serious data munging within your code, 98% could be off by an order of magnitude. That work might be number crunching in the core of a mathematical modelling application, more advanced UI such as parsing a written language or rendering a complex visualisation, other I/O with non-trivial data processing like encryption, compression or multimedia encoding, and no doubt many other fields too.

Generalising from one person's individual experience is always dangerous in programming. I've noticed that developers who come from the DB/business apps world often underestimate how many other programming fields there are. Meanwhile, programmers who delight in mathematical intricacies and low-level hackery often forget that most widely-used practical applications, at least outside of embedded code, are basically a database with some sort of UI on top. And no, the irony that I have just generalised from my own personal experience is not lost on me. :-)

This can lead to awkward situations where practical problems that are faced all the time by one group are casually dismissed by another group as a situation you should never be in that is obviously due to some sort of bad design or newbie programmer error. I'm pretty sure a lot of the more-heat-than-light discussions that surround controversial processes like TDD ultimately come down to people with very different backgrounds making very different assumptions.

Redesign and refactoring is often related to quality, aren't they?
I suppose, but what is the value of untested code? This sounds like an excuse for coding without testing.
There are many ways to improve code quality. Using an automated test suite is only one of them, and while it's one that is widely useful, it is of very limited value in some circumstances and I think for some developers it instils a false sense of security. Not having an automated test suite that covers a particular part of your code does not imply that the code is "untested" or of no value. It just means some other approach is needed in that case.
Not having automated test covering a piece of code does not imply that it's untested at the time it's written, but it sure as hell implies that it's not getting tested when seemingly unrelated feature X gets refactored and unknowingly breaks it.

Tests are only marginally important at the time you're writing the code they test. The real value comes months later when something else causes the test to fail, and now you a: know the code is broken, and b: have a clear specification what what that code was supposed to do.

Sorry, but I simply can't agree with most of that. I do agree that automated tests are more valuable during maintenance than during initial development, though I think they help then too. It's the other details of your comments I'm disputing below.

Firstly, even if automated testing isn't appropriate for a particular part of the code, there should still be other forms of quality checking going on that would pick up a broken feature before the code is accepted, and certainly before the product ships. If this doesn't happen, you're relying on a limited set of automated tests as a substitute for things like proper code reviews and pre-release QA, in which case IMNSHO you're already doomed to ship junk on bad days.

Secondly, if you can break one piece of code by changing a completely unrelated bit of functionality elsewhere, you have other fundamental problems: your code isn't clearly organised with an effective modular design, and your developers demonstrably don't understand how the code works or the implications of the changes they are going to make before they dive in and start editing (or even afterwards). Again, you're already doomed: no amount of unit testing is going to save you from bugs creeping in under such circumstances.

Finally, unit tests are not a clear specification of anything, ever, other than the behaviour of a specific test.

Basically, if you consider automated unit testing a substitute for any of

(a) maintaining a clean design

(b) doing an impact analysis before making changes to existing code

(c) writing and updating proper documentation, including clear specifications, or

(d) proper peer review and QA processes

then I think you're suffering from precisely the false sense of security I mentioned earlier. In many contexts, unit tests can be great for sounding alarm bells early and giving some basic confidence, but even in the most ideal circumstances they can never replace those other parts of the development process.

QA itself is a process failure. If the testers have ever repeated an action more than twice, they should be automated, and you're back to automated testing.

The only QA I've ever worked with that was worthwhile spent their time writing automated tests - they were programmers concentrated in test. Otherwise, you're literally saying 'It would be cheaper to pay this room full of people to do what a machine can do instead of paying 1/10th their number to write the same thing as a test', which is essentially never true.

I think you can go one step further. Never forget you're serving your customers, and your software has other raison d'etre. You only write software to provide value to them, so think of testing the same way.

Each test has the opportunity cost of writing some part of a new feature for your customers. But so does every minute spent of fixing bugs that would have been caught with more testing, at a fraction of the cost.

I don't believe anybody that says they test all functionality. Most? Sure. All? No way. Not in a non-trivial codebase.

Article about the group that writes the space shuttle software, sort of relevant?: http://www.fastcompany.com/magazine/06/writestuff.html

Well, there's `all` and there's virtually all. All is 100% branch and statement coverage, and is a big waste of time.

When I saw my codebase has 'all' functionality tested, I mean we don't commit code without tests included too. I think that's a pretty reasonable definition.

100% branch and statement coverage doesn't begin to cover "all". Consider:

  double sin(double x) { return x; }
Simply testing x = 0 gives you 100% branch and statement coverage, but I don't think you want to ship just yet =)
That's not entirely fair - you haven't tested the branches or statements inside the sin function.

However, even if the sin function is already tested elsewhere, you will still need further testing to ensure that you are calling it correctly (e.g. not confusing degrees and radians).

EDIT: Yes, I read it wrong - clearly need coffee...

The original poster gave an implementation of sin(), not a unit test. That implementation has no branches in the source and, for any decent compiler, will not have any branches on the machine, either.
Perhaps you need to read it a little more closely? For the given function, he has indeed tested all the branches and statements.
Also, it's the simplest thing that can possibly work! (And for very small values of x is also the best possible implementation.)
Even better: it's a correctly-rounded implementation for nearly half of the input space!
(comment deleted)
yep, you're absolutely right. You can get 100% testing coverage when you define it as "percentage of code executed when tests are run".

That said... that kind of coverage isn't quite as useless as it might seem. If your tests do execute every line, even in a completely contrived way, you will catch a lot if you change your code. You just tend to catch more of the "wrong number of arguments passed to a method" kind of error than "you are allowing the autopilot to try to land the plane 100 feet below the runway" kind of error ;)

Careful though with tests that literally execute every line of code: You tie your test to your implementation. That makes even the slightest refactoring difficult. Better to have unit tests that only care about the functional interface.
The trouble is that the poll doesn't have a middle ground between "all functionality" and "a few critical things".

A full run of our test suite literally takes months on a cluster of hundreds of CPUs (obviously, there are also faster versions of the tests which are run frequently). While I have a long list of additional test coverage that I would like to add, what we test is much closer to "all functionality" than it is to "a few critical things".

Would you be able to share what type of software it is? I'm curious what takes that long to run
I'm also working on some software which tests a lot of functionality, not just 'a few critical things' but certainly not 'all functionality' either.

I'd say that a lot of good responses would have been in between those two.

Agreed. Missing the option of "Most functionality", or "All functionality within reason". Without that option anything I select would be misleading.

I think it's safe to assume that anyone who selected "All functionality" actually means "Most functionality". Also I think we can assume that a good proportion of people who selected "A few critical" would belong in the "Most" bucket.

Sure, but "All critical, most important, and lots of trivial" wasn't an option.
Depends how you define functionality I guess. If you are talking about high-level user functions (create a new user, modify user, delete user), then lots of organisations probably do have tests for all functions.

However, if you consider functions on the code level (e.g. Java methods) then organisations with 100% coverage will be thin on the ground. If you go further and consider line coverage, almost nobody will have 100% coverage.

A common problem with organisations claiming to test all functions is that they will only test the happy path - there will be few tests for things like unexpected or illegal input etc.

People ... don't have tests? o_O In 2012?

I am seriously considering putting together a "Software Engineering for Small Teams" course or set of articles. With a little bit of expertise, you can inject testing in to most projects, use the minimum of Agile that'll help, and generally massively raise your game - and by that I mean code faster, better, and more reliably, with considerably less stress.

(edited: turns out I forgot which year we're in :-P)

Even more amazing... it's 2012!
Sometimes I'll forget what year it is too, or maybe that was just a typo. But my OCD just isn't letting me let this slide...

*2012

I think it all depends.

I used to always write proper full-fledged tests. Then I started my startup, building a product in the few hours left after a demanding high-stress job and a tumultuous private life.

Within a few weeks, I stopped writing tests. Within a few more weeks, I turned off the test suite.

I wrote the product, got it working, received market feedback, realized my model was all wrong, rewrote the entire domain model and UI multiple times all to finally realize that my component boundaries were all wrong and intuitively understanding where they should've been.

Now I feel confident about an architecture that will stay stable for 12+ months and each new component I write is properly tested.

In the meanwhile my lack of tests is starting to bite me very slowly, but I find that I'm just slowly replacing all 'bad parts' with properly tested components with clearly defined boundaries, rather than changing existing code.

And in the end I'm really happy that I decided not to test as much. It has it's place but when your time is really precious and you're trying to mold your software to fit the market needs, it just isn't worth it.

I don't know how many others are in a similar situation but, for me, sometimes it just ain't f*ing worth it.

Those are fair points all, and I feel cover similar ground to an article of mine:

http://www.writemoretests.com/2011/09/test-driven-developmen...

I'm always amazed by how well the whole 'technical debt' analogy holds up. Yes, leveraged development at the beginning is fast, and sometimes a good idea for getting to MVP. But the cost is still there, and will become apparent, and needs dealing with.

I'm doing a startup as well, and we do a fair bit of testing.

One of the keys to making that work for us is a short feedback loop. We automatically release on every commit, which means every couple of hours. Speculative features get minimally implemented; if they look good then we beef them up more. Our goal is to avoid not just the unneeded tests, but the unneeded feature code too.

I'm personally pretty happy with the testing in that we don't have to spend much time on debugging or manual testing. It's very nice to make a major change, poke at it a little bit, and then ship it with a fair bit of confidence that it will work.

I'd been working for years in a workplace that tests virtually everything up front until I joined a startup, and I agree with you.

Experimental features may be very short-lived, or require extensive tweaks, and the technical debt that accumulates from not testing may never arise over their lifetime. Once you're sure it's going to stick around forever, do it right and cover it with tests.

I think you'd find out most significant software has some kind of testing, but if you inherit a quarter mil lines of code, need to make one focused change, making the case to spend six months to write full coverage just does not get funded.
I'm a lot less surprised. Not everyone gets to work on a shiny new codebase which was created after regular testing was the norm. A lot of us work on maintaining code that's 5/10/20 years old, and have to worry about things like maintaining and adding functionality over refactoring the entire codebase to support unit tests.

When you're in this position, your going to get more value out of creating a smaller set of functional integration tests that cover the critical functions of the project. Sure, adding new tests as you add functionality is a good idea, but it's not going to result in total coverage for a very long time.

The very first thing I do when I take over a codebase is to write tests. Without tests, it's impossible to do maintenance work or add functionality in any sort of rigorous fashion--how can you know that your assumptions about how the code works are correct? How can you know that your trivial change didn't break something?

Of course, tests don't actually tell you these things. But they can tell you that your assumptions were wrong, or that your trivial change broke feature xxx, and that's crucial information to have.

Do you always have the time/bandwidth to write these tests? I'm curious what you might do if an old codebase lands in your lap and someone says "here, fix these bugs by the impossible_length_of_time."

I appreciate the idea here, and I've done the same in certain circumstances, but typically that means writing tests for bits of functionality that I need to touch.

If it's impossible, then the diligent engineer says so. Projects are often doomed by people with "can-do" attitudes attempting to achieve the impossible.
frobozz nailed it, really. If something can't be done, it is the engineer's responsibility to make that known to his manager, who is responsible for communicating that to whoever is asking for the work.
Saying something's feasible if you throw the tests out just doesn't make sense to me.

Without good automated, easy-to-run tests, you're going to blow more time fixing bugs and ad hoc testing in the long run.

Thinking you save time by not testing is a lie on all but the most trivial of projects.

Do you always have the time/bandwidth to write these tests?

Does an ER surgeon always have the time/bandwidth to scrub hands before surgery?

Does an ER surgeon always have the time/bandwidth to scrub hands before surgery?

Does it potentially take the surgeon several days to scrub before an emergency surgery?

Edited to add: I appreciate the analogy, but it's flawed. If someone comes to me and says "here, developer A is on holiday, and we have this bug that it causing massive disruption in the field," is it appropriate for me to say "well, I can do that, but it will likely take me five days so I can understand the codebase and write the appropriate unit test suite."

This is circumstance I thinking about, not necessarily inheriting a codebase and having to add features to it. In that case, certainly, I'm going to take my time, read the code, and write tests.

In this scenario, there should be tests already present covering developer A's portion of the codebase, together with documentation on how to run them (though tests should be as self-explanatory as possible).

In fairness, I recognize that this isn't always the case in the real world. Sometimes you really do need to just blindly attempt to fix something, and there's nothing to be done about it. But it should never become a regular occurrence, and you should never get comfortable doing it. First thing I would do is tell my manager exactly why I'm uncomfortable, and what a conservative assessment of the risk is. If we decide to go ahead with the change anyway, I would create two new entries in the bug tracking system, which should be developer A's top priorities as soon as she returns: thoroughly vet my changes, and DEVELOP A SET OF TESTS.

I see exactly where you're coming from, and I'm there all the time.

It just troubles me that people are so often willing (and eager!) to waste a lot of time doing half-assed manual testing when they claim not to have any time to write tests. Especially when the state of the art in test automation is better than it has ever been.

This has me thinking that the importance of test automation is related to the proposed frequency of changes. If someone wants a one-off change for something this very second I'll just change it. If someone wants me to inhabit a codebase for any length of time, I'll always set up tests for it. The problem is where you can't tell the difference between those two scenarios until it's too late.

Let's say you don't have the time to give it the complete understand-and-write-unit-test-suite approach though.

How are you verifying you fixed the bug otherwise? By changing some code, building the app, and running it to verify the bad behavior doesn't happen anymore? I don't really see how not writing a unit test (assuming the code is unit-testable in the first place) saves you any time. You are doing testing anyhow.

And if it was a critical bug, personally I'd want to feel as confident as possible that I fixed all permutations of it.

Of course working on a code base without a majority test coverage is dodgy (and intellectually frustrating), but it's a necessary skill.

I feel that it is unreasonable to expect that you will be able to pick up any code base and immediately write sufficient tests to get coverage on a majority of the code base. Speaking from my experience picking up old code bases, just being able to write isolated unit tests would require refactoring most of the code base, which is typically not something you will have time to do before you're expected to do other work.

I can't think of a single manager that I've worked for who would accept me saying, "it's going to take me 3-6 months of refactoring & building tests before I can start fixing bugs and providing enhancements."

> I can't think of a single manager that I've worked for who would accept me saying, "it's going to take me 3-6 months of refactoring & building tests before I can start fixing bugs and providing enhancements."

I can't think of a single developer I've worked with who would try that approach.

When a bug is identified in a project with few-or-no tests, the approach that I usually see taken is to write some sort of large, slow integration test that exercises bug, then fix that. That allows you to prove that the bug exists and prove that the fix fixes it, at least for the documented case(s).

There's no reason to cover an entire legacy code base with tests if you're only changing a small portion of it.

Who's talking about adding all?

My area of expertise is adding tests to legacy codebases. Obviously you're not going to hit the whole thing overnight. But that's no excuse for not having /any/

It depends what you're working on. If you've got a project that has to grow fast, adding more features than fixing bugs, not even knowing what you're going to keep in a few months, the time spent fixing regression bugs due to lack of tests I think is relatively little.

You may say "I'll thank myself later", but in this sort of business there won't be a later if we're not fast enough. It's a lesser of evils thing.

It would be nice if testing were a faster thing to do. The faster you can do it, the lower the threshold would be for this sort of a judgement call.

This is a fantastic idea - a lot of 'small teams' think that they are too small for extensive tests, or don't know how to organize themselves effectively.
When making a business, you have to continually make tradeoffs. Do I work on new customer features, do I work on customer acquisition features, do I fix bugs in old features, etc. Testing has value, but it often doesn't have the highest value. I totally agree about raising your game, but I can see how young startups especially race ahead without them (often to have them crash down on them 3 months later)
Where's the option for "We thoroughly and immediately test every change (and all affected processes) ourselves to also ensure UX is top notch"?
This. I basically do this because it's quick and does not need any extra code, esp because I write web apps and testing it is just a ctrl+S away. No compilation, just F5.
The day I changed one line of code and 100+ tests failed was the day I really got it.
Although that could just be a sign of brittle tests....
I don't always test my code, but when I do I do it in production. Stay thirsty my friends.
I don't always test my code, but when I do I do it in production.

Stay thirsty my friends.

On my latest project (Rails 3.1) I test thoroughly the back end code, but only in a limited way the CS front end code. I'm using jasmine there, but that is a lot of overhead.
We would like to test a lot more but I really don't know how to test some of the critical stuff.

Just as an example, how do you test a parser that processes large amounts of sometimes sloppy semi structured text? Whether a particular defect should be classified as a bug in my parser or as a rare glitch in the source data is undecidable until I know how often the defect occurs.

What I need is a kind of heuristic test framework that makes sure the parser doesn't miss any large chunks that I only find out about weeks later if at all. I cannot supply individual test cases for everything that could possibly be found in the source data.

I cannot supply individual test cases for everything that could possibly be found in the source data.

Perhaps not, but you can supply test cases for known problems you might encounter, as well as ones you've solved after they've been encountered.

Yes, that's what I'm doing, but I feel it's a drop in the bucket.
It is, but as bug crop up you can add tests to ensure they don't crop up again. While it's not possible to ensure perfection, it does help ensure you don't 'revert back' to past problems.
Also don't forget the tests you add help you with the regression tests. The large set of tests would assure you that the new fix you do will not lead to any other bugs that you had fixed earlier.
I write web apps and I don't do any testing at all. I am also a unit testing newbi. I just run the app and make sure what change I made works. No automated testing what so ever. it just works and I believe it will be an unnecessary over head Is this bad? If yes, how can I unit test my JavaScript?. Plus i always thought UT is for code that compiles, right?
Look into Jasmine BDD for js testing. We use it for all our js and it has greatly improved quality and reliability.
I went through an experience where 2 years ago I thought "I hate unit testing, don't know how to do it and don't see the value". 2 years later I think "I enjoy unit testing, know how to do it well, and see the value in unit testing _most_ of the time".

I believe this transformation is entirely to do with the fact that I paired with a brilliant developer every day for 6 months who really helped to answer all my questions and show me how to test a variety of different things. I truly believe that unit testing (and testing in general) is a hard thing to grasp without being able to learn from someone over a long(ish) period of time.

I realized that I hated testing because I didn't know how to do it and wasn't good at it. I also didn't understand what the essence of a unit-test was; my tests would often cross multiple integration boundaries (ie: hit the db and the server) and were really more like bloated integration tests. Once I had sorted that out and was able to see a variety of techniques for testing specific scenarios I realized that I actually enjoyed testing and the satisfaction of knowing my code was covered against defects started to be a big motivator.

To answer your specific question about JavaScript testing, I've been using JasmineBDD[1] for the last 2 years and have found it a joy to use. It really makes testing things easy and has tools that allow you to isolate your tests down to the individual units.

[1] http://pivotal.github.com/jasmine/

I paired with a brilliant developer every day for 6 months who really helped to answer all my questions

Sounds great. What else did you learn?

Lots! How to test-drive (as opposed to test-after), how to mock out integration points, what mocks/testdoubles/spies are and how they differ. I also learned that Enterprise Java is a particular level of hell. In all, it was a good experience though :)
I used to get code back from developers EACH AND EVERY TIME with massive bugs like: unable to register, unable to login, unable to add content. I wrongly assumed that they at least ran through and checked for any bugs they introduced before sending me the new code. So each and every time I got code back I had to go through manually and check it, sign in, log out, register, add content, delete content, edit content, add category, etc...

I wish someone could make a simple service that allows me to set up my web app, set up test parameters that it tests each and every time, and tell me if it failed or not. I want to automate my babysitting.

I think you could use something like like selenium? Should be fairly easy to test the basic things you would normally do manually.
I think https://stillalive.com/ will do what you want.

Their landing page is a little weak but I dig their UI for setting up tests.

Selenium is not the prettiest tool out there, but as the homepage says, Selenium automates browsers, making it ideal for running tests that you describe:

http://seleniumhq.org/

You should check out saucelabs.com
Please don't fix this with a technical solution.

There is some reason that your developers aren't engaged in the work. Figure out why they don't care about working code or the user experience and fix that.

If you plug the obvious holes, you won't have fixed your quality problems; you'll just shift them to the places where you won't notice them right away.

Please don't fix this with a technical solution.

I can't agree more with wpietri above. There surely is a non-technical problem at play. That isn't to say that you shouldn't try to automate your babysitting, but if your need for babysitting is that severe, you have other problems.

Thank you both. I will not forget these pieces of advice.
that's exactly what we offer at http://testingbot.com

you can upload a bunch of Selenium tests, indicate when you want to run them and we'll send alerts if a test fails.

Honest question: Do you believe that test == automated test?
Currently working a webapp with a legacy(2002ish) java code base with a fair amount of testing but it's not even close to full coverage. JMockit has gone a long ways to towards making it easier to expand the test coverage but it's difficult to find time to make significant impact.
Another interesting question: how often do your tests run? Most folks probably run unit tests with continuous integration but what about functional and performance driven tests?
Most tests (unit, integration, etc.) are triggered when new code is checked in. For other kinds of tests, we use schedule triggers to run them at a particular cadence, either overnight or more frequently if that's what needed.

TeamCity is good for automating with both kinds of "triggers".

Continuous integration should run all your functional and performance tests if possible. Each "unit" (could be a commit or a push or a merge depending on your philosophy) can cause errors, and being able to pinpoint the unit in which the fail happened is immensely valuable.

If you have something really long running (eg you make a database and have a two week test), then you may be able to minimize your test (possibly automatically) and use git/hg bisecting to find it.

Fuzz testing (finding holes in your code) can be run separately, and again, you can find the root cause through minimization and bisecting.

We don't test code where I work, but I'm strongly for testing.
Unit testing is the salvation of anything but the most trivial of projects.

Sometimes, it can be frustrating to have to stop and write a unit test before send out something for further review, but computers are more reliable testers than humans are.

Learn, use, and love unit testing, or face the consequences.

Where is the "we'd like to do more testing" without the negative excuse option? :)
I don't test as much as I probably should, because it seems cumbersome since I am mostly dealing with APIs like Facebook. For example, if a user revokes their Facebook OAuth app token they get an email notification about that from me, informing them that the app will no longer be able to function because of the expired token.

I am not automatically testing that, perhaps I am missing something, but automating the steps to log in to Facebook and revoke the token and then also making sure that SendGrid sent the email correctly just seem impractical.

You don't want to test external APIs. You probably do want to test how your application behaves in response to using the APIs. One way is to mock the API calls with canned responses. Another way is to use a tool like VCR (https://github.com/myronmarston/vcr) to record and playback API interactions.
Manual testing has it's value as well. Automated testing isn't always cost-effective or simple.
Personally, I do end-to-end tests for some basic cases just to make sure everything works together.

But most of the tests are more fine-grained. So in your example, I'd test the core logic against fake Facebook API responses and a fake outgoing email call. That lets me easily test some of the weirder cases. E.g. if Facebook breaks, will the job skip that user and keep going rather than blowing up?

"I don't always test my code, but when I do, I test it live."

I wish I wrote tests more. Maybe I'm to impatient. :(

You probably just don't know how to test properly yet. Testing should ultimately give you more time and make your life easier. If it's making it harder or making you take longer (unless it's a really trivial task), it's not being done correctly (yet).