views:

527

answers:

12

Ok, I know I am going out on a limb making a statement like that, so my question is for everyone to convince me I am wrong. Take this scenario:

I have method A, which calls method B, and they are in different layers.

So I unit test B, which delivers null as a result. So I test that null is returned, and the unit test passes. Nice.

Then I unit test A, which expects an empty string to be returned from B. So I mock the layer B is in, an empty string is return, the test passes. Nice again. (Assume I don't realize the relationship of A and B, or that maybe two differente people are building these methods)

My concern is that we don't find the real problem until we test A and B togther, i.e. Integration Testing. Since an integration test provides coverage over the unit test area, it seems like a waste of effort to build all these unit tests that really don't tell us anything (or very much) meaningful.

Why am I wrong?

+3  A: 

Unit testing is finer grained and allows you to pinpoint errors. What if A or B failed? How would you know which one failed if your integration test failed? And that's only 2 methods -- imagine if you are integration testing a front controller of a web application, which loads a handful of models and calls a bunch of methods. That would be a nightmare trying to track down exactly what went wrong.

ryeguy
Again, I can examine all of these in the debugger to see what caused the failure. The alternative is to mock the "loads of models" and hope I have covered every scenario.
CodeGrue
+13  A: 

Here's an article on test categorization with some arguments

I won't mention the benefits of testing as a whole, once we're just comparing unit tests vs. functional tests:

  • Unit testing helps you reduce the scope where to look when there's an error. - Let's include classes C, D, E, ..., Z in this scenario. If you have only integration test and it fails, where do you start looking? If you don't have unit tests, you would need to look everywhere inside each of those classes AND the "wiring" between those (which is a narrower scope).If you had unit tests, then you'd just need to check the wiring. In this case, it would also be a bad thing not having some smaller integration tests, like testing A, B, C and D only (so you already know if the "wiring" between those are in fact working).
  • With unit tests, you fail faster. This is even more true if you TDD. You probably write your tests after you created all of class A and B. When you run your test, the way A and B works is not as fresh in your mind (maybe you wrote them the week before - hopefully you don't start testing only when the product is "finished"). You must then remember what you were thinking when you wrote those. Also, unit test are faster, so you're more likely to run them more frequently (perhaps run them automatically every time you save?)
  • Unit tests provide a better documentation how your class should behave. If you're a "normal programmer", you probably hate writing documentation. This forces you to write documentation while programming, and forces the documentation to never be obsolete (if it is, your tests fail). And it also helps when you need to change somebody else's code.

In the ideal world, when a test fails, you won't need more than 2 minutes to know what to look (with no need of debugging). The idea of having tests of all sizes is just a guideline to achieve this goal, rather than spending hours/days/weeks debugging =).

Samuel Carrijo
so instead of debugging for a few hours, you spend days and days conceiving every possible test which may or may not fail. On top of this, a lot of things are not easily testable. like network data and gui's. I understand unit testing is the fashionable thing to do at the moment when you are fresh out of school. But as I said in a previous comment, sometimes it is just better to use that time to be more productive ;^)
Toad
But with a debugger, I can navigate through the methods to see where it failed. It seems idealistic to assume I need to ignore the degugger.
CodeGrue
I'm not against debugging. It's needed at times. But to create a unit test for every method in your code (of which 99.9% will never fail) so it saves you 5 minutes debugging is just plain silly. It seems like a lot of people are afraid of debugging. With the current tools, nothing is easier.
Toad
This is like the old joke about the guy who lost his keys a block away, but he's looking here because the light is better. Integration is where things break. Test-driven development makes a lot more sense when it's based on integration tests, not unit tests.
Cylon Cat
@CodeGrue _Ideally_ you won't need the debugger (that's not always true)
Samuel Carrijo
Tests aren't expected to fail frequently, so it makes little sense to optimize them very much for fast diagnosis of failures.
Michael Borgwardt
@reinier You'll take longer at the beginning, but you'll be glad if the developer of the class you need to change wrote those tests
Samuel Carrijo
Also, network, databases and GUIs are usually functional testing stuff. Unit tests are focused more on logic bugs (most common type)
Samuel Carrijo
+3  A: 

OK, unit tests won't find every problem, that's why we have integration tests, too!

But suppose you have a method that must return a value between 1 and 9 and you write a test for it and find that it returns a value of null or 10, then you know the code is broken long before you get to integration testing.

HLGEM
+4  A: 

Unit tests are not for testing what should be tested in integration - they are complementary sets of tests. Unit tests guarantee that a given unit of code on its own performs what it was designed to do, nothing else. Integration tests make sure all your units work well together to perform what the overall requirements asked.

Otávio Décio
+1 Had your integration test example *passed*, then you'd never know that either A or B are *not* delivering their expected results.
LesterDove
My argument is that integration testing also catches the functioning of the units.
CodeGrue
@CodeGrue - it may or may not. Anyways, if you don't see any value on unit tests, just leave it alone. If it works, great. Nobody is saying that unit tests are *required* to make a system work, only that they may give you a better chance that it happens.
Otávio Décio
+2  A: 

I think the major reasons are:

1: unit-level tests give you more information about what's failing.

2: you can't run integration tests until your components are integrated. The sooner you find a bug, the easier/cheaper it is to fix.

Mark Bessey
+3  A: 

I agree with you that in your simple case, there is no need for unit tests. Testing (unit, integration, functional, regression) are all dependent on the size of your project and the number of people involved and the level of experience of everyone involved.

As any of these factors increase (well, the last one would have to decrease), then the need for testing increases. You need to find a solution that is appropriate and applicable to your particular project.

I am a big fan of unit tests and believe that all developers should write and automate them regardless of the size of the project. They will ensure that your code is bug free and will really help when you return to the proejct after several months.

I also believe that as you have multiple people working on the project over an extended period of time (read anything other than a throw-away project) then you also need automated integration tests to ensure that the various components work together.

yamspog
+1  A: 

Most of the problem is in the scenario you've posited -- according to your description, all B does is return null. Unit testing something that trivial probably is pretty pointless -- but is is writing the code to start with. If all you need is a null, use null directly and eliminate B completely. If B justifies its existence by really doing something more than return null, then the unit test you've described is incomplete, and you need to test whatever else B is supposed to do.

Jerry Coffin
The assumption is that B does a bunch of work to determine null is the proper response.
CodeGrue
@CodeGrue:In that case, you need to test it properly doing the bunch of work.
Jerry Coffin
A: 

If A is a query that runs for 2 hours, and prepares data for B, which is another query that runs for 2 hours, why should I wait 4 hours to find a problem when I could wait 2? It's not like this is a new idea. Do you think car companies assemble the entire car before seeing if the engine works?

brydgesk
But in my scenario, you would test for 2 hours, then test another 2 hours, then not find that there was a problem until testing another 4 hours.
CodeGrue
With unit testing, you run module A (2 hours), fix the problem, rerun module A (2 hours), run module B (2 hours), assemble, run the whole thing (4 hours). So you have a total of 10 hours, versus 8 without unit testing. So unit testing hardly adds that much time. But there's nothing magic about the 2 hours I chose. As B gets longer, it gets better.
brydgesk
@brydgesk: Not to mention that you're assigning zero time to fixing the problem. It will almost certainly take less time to fix a problem found in a unit test than one found in an integration test.
David Thornley
Very true David. Plus, that 2 hour difference could very well be spent just trying to figure out which module actually failed.
brydgesk
+2  A: 

Another reason that I haven't seen anyone else address: You will not always be the one to maintain the code you write. Let's assume I've written A & B in separate modules, so that each is in a separate jar & has its own maven build file. Let's further assume that A is a Dao method, and B is some sort of Service layer. If I have written even a basic unit test for my Dao (perhaps using test driven development, even!), I have much more confidence that future changes to the database structure & query that may effect my dao are caught early. If, for example, on my team, another developer needs to add columns to a database table that my dao uses, I certainly want them to know right away if doing so will break my dao methods. If they have to wait to build all layers & run an integration test to verify that, they're just wasting precious development time, in my opinion. As a developer, I'd much rather run a 10 second unit test 10 times to fix & test any breaks I find than to run a 2 minute integration build & integration test run multiple times. Obviously, the times vary greatly depending on the size of the project (and your hardware), but those are the kinds of times I'm dealing with on my current project.

elduff
+2  A: 

Unit tests aren't about debugging during initial development. They're about design, and they're about adapting to change. My unit tests almost never tell me where a bug is when I'm on my first pass through a hunk of code. My tests warn me when a bug arises six months later because somebody (possibly I myself) tweaked an object whose relationships they didn't fully understand.

Saying unit tests are a waste of time during initial develoment is like complaining that my car gets crappy mileage on its way out of my driveway.

Jim Kiley
+2  A: 

Reinforcing(hopefully) Samuel Carrijo's post...


When choosing between: 1) unit tests AND a small integration test vs 2) just the integration test you are making a calculated bet. If the integrated component is complex then any failure in the integration test is going to be tricky to figure out to find without unit tests. Sometimes I get it wrong, get a test failure in the integration test and start writing the unit tests without trying to debug. If you bet on not needing unit tests and win, you can save yourself a fair amount of time, and still have confidence in your code, but you don't get the "specification" benefits that unit tests provide to the next guy who has to touch your code.

phlip
+1  A: 

Integration tests suffer from a form of combinatorial explosion: Say method A has 5 different ways to behave (different edge cases, etc.), and so has method B. If you test them together, there are now in theory 25 different cases you have to test! Yes, many of them may end up being the same, or can't occur for one reason or another, but basically, the more things you test together, the more impossible it becomes to test all edge cases.

Michael Borgwardt