Live data from Hacker News

Where unit testing fails

hmemcpy.com

21–30 of 52 posts

Re: Where unit testing fails

#21
post #8

Earlier quoted context omitted.

> The whole concept "test driven development" is hocus... I've worked on numerous projects where TDD has (and continues) to prove invaluable. So regardless how much experience you've got, I've had experience to the contrary, thus the "whole concept" isn't hocus. Why have you come to believe this?

It's hocus where contrasted to developer-driven testing, because Test Driven Development - as a development methodology (as opposed to a tool) - espouses that you Write All Your Tests first. Have I had experience (and much value) out of sometimes writing tests for certain problem classes before writing any code? Yes. Changes to existing functionality are often a good candidate. Does TDD as a methodology suggest you s…

I've always thought of testing as simply a way to verify that your code does what is supposed to do.

I tend to agree with you that TDD as a development methodology is deeply flawed. I have never found that the act of writing a test case and making it pass has helped me with the actual creative process of solving a problem via code. In fact, I'll frequently dash out a method I'm writing quickly, and then go back and reconstruct it piece by piece via TDD.

The main benefit writing tests first gives you is that you know your test is actually working (i.e. because it first fails) and that the code you implement actually passes it. It also keeps you honest because it prevents you from writing "extraneous" code, i.e. code that exists but is not specified by a test.

So yeah, I totally agree with you that the cult of TDD kind of sucks. But I haven't stumbled upon a better way yet. I've often thought it would be cool if there was some way for e.g. your version control software to be tied to the code you write that passes certain test(s), such that you could somehow verify that without that code your test fails and with that code your test passes. For the present though, TDD tends to work pretty well for me.

Re: Where unit testing fails

#22
Maybe I'm in a mad mood or somth (it's Monday, after all) but all this "let's write a parallel world, called TDD, to which the actual production code has to comply and praise and give sacrifices"-mantra has gotten a little over the top.

Don't get me wrong, I'm sure that all these guys and chicks that live and die by TDD are smart (probably smarter than me), but what they are building is starting to get more and more out of touch with real-world requirements (I wanted to say somth about how this resembles a schizophrenic world, but for the moment I'll stop short of that).

My father was a civil engineer, at a time when they didn't have AutoCAD available and all that fancy computer stuff. As he elegantly put it: "if I mess things up in my work people will die, either in 2 weeks or in 20 years, when the next big earthquake will hit us". Well, I never saw him "testing" building apartment-blocks, or industrial buildings, or roads, he made sure that what was on the sheet of papers where the construction plans were drawn would get built as accurate as possible in real life. And keep in mind that he was managing construction workers, many of them close to illiterate, even former convicts, and not CS-graduates. That's the job of all civil engineers from all over the world. So, if they can do it, why in the name of God do we programmers write tests for mundane stuff like sudoku-solvers? Something is not right.

Re: Where unit testing fails

#23

I can't help but appreciate the irony of a link titled "Where unit testing fails" leading to a WordPress database connection error.

So you think unit tests would help with that?

That's the point. Most unit tests wouldn't stop this kind of problem. ;)

Re: Where unit testing fails

#24
post #20
post #14

Earlier quoted context omitted.

> Writing tests before code works pretty well in some situations. Test Driven Development, as handed down to us mortals by Agile Testing Experts and other assorted shills, is hocus. What? So it works in some situations, but not all. Thus hocus? I'm no 'Agile Testing Expert', but I have worked for the last 2-3 years doing TDD exclusively (as in ALWAYS writing tests first). It isn't hocus. It is effective. I can unders…

Excuse me for interrupting the budding flame war, but I have a question regarding the subject, which you seem qualified to answer? :-) My experience is the same as (what I believe is) the usual criticism of tests-first. I often need to rethink the API to my functions/methods, sometimes more than once. This goes for both external and internal APIs, so it doesn't help with detailed interface specifications between modu…

One benefit to TDD is that you become a user for your API much more quickly, and so your API doesn't need to change as much.

TDD suggests 3 steps: 1. Write one test 2. Write the minimal code to make it pass 3. Refactor the code you've got.

Try not to think of tests as immutable. You can throw away tests when they don't provide value.

Finally, make sure you're using an IDE with refactoring support. That helps immensely when you make an API change which will affect your tests.

Re: Where unit testing fails

#26
post #20
post #14

Earlier quoted context omitted.

> Writing tests before code works pretty well in some situations. Test Driven Development, as handed down to us mortals by Agile Testing Experts and other assorted shills, is hocus. What? So it works in some situations, but not all. Thus hocus? I'm no 'Agile Testing Expert', but I have worked for the last 2-3 years doing TDD exclusively (as in ALWAYS writing tests first). It isn't hocus. It is effective. I can unders…

Excuse me for interrupting the budding flame war, but I have a question regarding the subject, which you seem qualified to answer? :-) My experience is the same as (what I believe is) the usual criticism of tests-first. I often need to rethink the API to my functions/methods, sometimes more than once. This goes for both external and internal APIs, so it doesn't help with detailed interface specifications between modu…

According to TDD writing inside-out is one of the points it tries to combat.

The point of using TDD to write interfaces is that you first write the code you wish you had, and then implement it. The other way around you could end up with code that is optimized for the code inside the module, and not optimized for using it.

Of course now you have to rewrite your tests and your code when changing an API, but at least you know that no other part of your application got broken by the new API.

I personally like the TDD approach to write interfaces, since then I won't take shortcuts just because it is easier to do in the specific module that I'm programming/extending. It will actually produce code that can be used easily from the outside and removes a lot of dependencies in the process.

Re: Where unit testing fails

#27
post #22

Maybe I'm in a mad mood or somth (it's Monday, after all) but all this "let's write a parallel world, called TDD, to which the actual production code has to comply and praise and give sacrifices"-mantra has gotten a little over the top. Don't get me wrong, I'm sure that all these guys and chicks that live and die by TDD are smart (probably smarter than me), but what they are building is starting to get more and more…

Civil Engineers these days (or this is my understanding, anyway) DO use AutoCAD and 'all the fancy computer stuff' because testing allows you to code more productively, when you do it right. That's why engineers these days do automated simulations...

At the point where you're writing tests to impress other programmers (see article) about how much of a 'craftsman' you are, you lose. When you're writing tests that help you as a developer, and help get the software out more quickly and with higher quality, then you're doing it right.

Re: Where unit testing fails

#28
post #18
post #8

Earlier quoted context omitted.

> The whole concept "test driven development" is hocus... I've worked on numerous projects where TDD has (and continues) to prove invaluable. So regardless how much experience you've got, I've had experience to the contrary, thus the "whole concept" isn't hocus. Why have you come to believe this?

The issue/argument here is not that writing tests is worthless but that in many cases the approach of "if we write enough tests then the code that solves the problem will eventually emerge" is doomed to failure or at least results in an extremely verbose approach to development.

But then I think the approach is the wrong one. In TDD you are not required to write a lot of test and hope that they magically work in the future. Instead a small test to do one thing should be written, and then write enough code to make it pass.

For example lets take a simple newsletter signup form with only a field to enter an email address.

Now you shouldn't write a test to see what happens when you enter an email, a wrong email, if the confirmation email is sent, etc. Start with the simplest case: "If I fill in my email, and I press klick, then it should show me a confirmation that I singed up." Not more. Now make that pass, and you have working code!

Now you can add tests that verify that a wrong or empty email is rejected, now make that pass. More working code!

With TDD you should never end up with a lot of tests and no code that solves the problem. In that case you are actually writing too many tests and not enough code.

Re: Where unit testing fails

#29
post #6

There are a couple tricks to effective unit testing. Ultimately though, the goal is to write non-brittle tests. That is, tests that won't break due to unrelated changes. Get this wrong and the cost of maintaining your tests will outweigh the benefits. Achieving this, in my experience, comes down to disciplined used of mocks and stubs. I've seen people lean too heavily on them, or not heavily enough. The biggest probl…

Can you expand a bit on the distinction you're making between mocks and stubs, and why you believe stubs are both underused and useful? Perhaps I'm doing it wrong, but I've found in my experience that bugs I miss in testing increase proportionally with my use of mocks or stubs. Invariably, when I'm forced to stub something and replicate its behavior I miss something subtle that comes back to bite me.

I think there's 2 problem.

First, people rely too heavily on mocks/stubs/fakes/(whatever you want to call them). This has gotten better in the past 4 or so years (in my mind, largely because of Rails and it being "acceptable" to hit a DB in a test - though it might actually predate Rails). I think this problem is pretty straightforward to understand (again, especially when you look at the Rails way to test a DB interaction versus a more "traditional" way).

However, there are instances where mocks/fakes/stubs are important. Some outside dependencies might not be accessible during testing, might not be predictable/deterministic, might be too slow or might simply involve too much setup. Also, solely relying on "real" implementations can also make your tests too brittle. Why should the logic for can_legally_drink? break if the DB column is changed from DOB to dateofbirth (ok, that's an extreme and poor example, but you get the idea, hopefully).

Anyways, assuming you agree that sometimes a fake is simply better, you get into mocks vs stubs. To me a stub is dumb and a mock is strict. Stubs also automatically reply with canned answers, mocks don't. A mock is used to assert that a certain expected call was made. A stub is used just to get your test to move over a line of code.

The problem is that people use mocks over and over again. Re-specifying the same expectations in every test..making X tests break when you change the behavior of the interaction, versus just 1 test. As semi-pseudocode (and yes, it might be better to just hit the DB in this case):

  def self.login_user(name, password)
    user = Store.get_user_by_name(name)
    return user.nil? || user.password != password ? nil : user
  end

To me, you kinda wanna check that the above code properly interacts with someStore. This is when you use a mock:

  it "gets the user from the store" do
    Store.should_receive(:get_user_by_name).with('leto')
    User.login_user ('leto', 'ghanima')
  end
You also want to make sure the password matching works. This is where people go wrong. They'll use a mock (strict) again..which'll just repeat the above code...except this test has really nothing to do with how store...we just want to get over the line of code:

  it "returns nil if the passwords don't match" do
    Store.stub!(:get_user_by_name).and_return(User.new)
    User.login_user('leto', 'ghanima').should be_nil
  end
If you are familiar with jMock, it's kinda the difference between allowing and oneOf. oneOf is very strict, allowing isn't. You really should use allowing whenever you aren't explicitly testing the interaction (and you shouldn't explicitly test the interaction in more than 1 test (dedicated to testing said interaction)).

Some framework even return smart canned answers. So instead of returning nil, they'll default to a new instance of the type, or an empty array, or some default scalar value (like an empty string). This is particularly useful when you just want to get passed a null reference (which is exceedingly common).

Does that make sense?

Post reply on HN