90

I assume that my project is decoupled enough to allow for unit testing. But how big, exactly, in terms of clases and functions does my project need to be to make unit testing worthwhile?

We all make mistakes and no one's perfect, but I consider myself a decent programmer to handle small projects' errors with stepping through. Or is unit testing a hard necessity no matter of what size your project is?

samthebrand
  • 368
  • 2
  • 12
  • 27
Lamin Sanneh
  • 4,025
  • 26
    The big incorrect assumption you're making is that the project is only for you and that it's only for now. What if you leave it for a few months, do something else, then come back to it? Will you have the same confidence about being a "decent programmer" (which has nothing to do with whether or not to test)? What if someone else takes over your project...? A great blogger (sadly no longer active) wrote that you should always "cater for the code reader, not the code writer". – Amos M. Carpenter Aug 08 '12 at 08:41
  • 6
    How about this (meant for C#.NET): int x = 3 * (1 / 3); what is value stored in x after the operation completes? My point is even one line code may require testing because it could lead to a bug, either because you don't know or because you know but did the wrong code. – NoChance Aug 08 '12 at 08:46
  • 2
    @aaamos, I think you missing my point. The fact that I am asked this question alone shows that I consider catering for the code reader as well. – Lamin Sanneh Aug 08 '12 at 08:51
  • unit testing is not for development but for maintenance... –  Aug 08 '12 at 09:25
  • Lamin, my apologies, I didn't mean to berate you, just to point out what I thought you were assuming, based on what you wrote. @Thorbjørn: Like so many things, that depends on how you use it. I use unit testing as part of my development. (Ok, fine, I meant I try to do that, anyway. ;-)) – Amos M. Carpenter Aug 08 '12 at 09:48
  • @aaamos in that case you ARE actually using unit tests as runnable documentation for future maintainers of your code –  Aug 08 '12 at 09:54
  • @Thorbjørn: I typically go back and forth between writing code stubs, writing test stubs, writing code and writing tests. Having tests set up correctly lets me know what I'm aiming for and also when I'm done (all the lights go green), but I've never figured out how to write complete tests before I write the code, so for me it's a very iterative process. Test a bit, code a bit... there are instances where that doesn't work well, but usually it does. – Amos M. Carpenter Aug 08 '12 at 09:59
  • 1 line of code? ;) – Wayne Werner Aug 08 '12 at 11:41
  • 14
    Having done it both ways, I can tell you its so much easier to write the tests as you are going along (i.e. while the project is small) than to go back and write unit tests later when you feel the project has met some arbitrary "we should write tests now" condition. – Wonko the Sane Aug 08 '12 at 12:06
  • @OlivierPons: Clearly you didn't read his first sentence... and thanks for taking it out on me. ;-) But if it bugs you so much, feel free to blog about your version of perfect code or about common fallacies. (I happen to have looked at some of Ramon's code and I can tell you that he knows what he's talking about. But that's as far as I'm willing to go in this conversation.) – Amos M. Carpenter Aug 15 '12 at 09:17
  • My bad, I remove all my useless comments. Thanks for pointing this out ;) – Olivier Pons Aug 15 '12 at 11:48
  • 1
    You're asking the wrong question. It doesn't matter how big the project is. The question is, how much do you care if it's correct? If correctness is important, than unit tests are an effective way to improve correctness. – Mark E. Haase Jan 14 '13 at 06:13

12 Answers12

213

Your project is big enough already.

In my experience, one class and one function have been sufficient to consider the need for unit testing.

    class Simple {
        boolean reallySimple() {
            return true; // how do we make sure it doesn't change to false?
        }
    }
class SimpleTest {
    void assertReallySimple() {
        // ensure reallySimple return value isn't changed unexpectedly
        UnitTestFramework.assertTrue(new Simple().reallySimple());
    }
}

gnat
  • 21,213
  • 29
  • 113
  • 291
  • 24
    Surely you should start earlier, at the 0 class/0 function point... if you're following TDD :) – Kaz Dragon Aug 08 '12 at 12:52
  • 117
    +1. If you program is big enough to have bugs, it's big enough to have unit tests. – Jason Orendorff Aug 08 '12 at 14:03
  • 9
    @JasonOrendorff: I'm mocking that up into a poster and putting it in my office. Brilliant. – Justin ᚅᚔᚈᚄᚒᚔ Aug 08 '12 at 14:50
  • 1
    Additionally it allows for "safe" refactoring. – Timo Aug 08 '12 at 15:43
  • I would even design Class pattern first then write every test units with each expected results then do the devs. – TecHunter Aug 08 '12 at 15:51
  • I was going to say at least 5 lines of code. – monksy Aug 08 '12 at 21:23
  • 8
    While I don't necessarily disagree with the sentiment, if your code is really that simple you could probably get away with just assertions instead of full blown unit tests. – Chuu Aug 08 '12 at 21:26
  • @Chuu "get away with just assertions" hm. For internal stuff this makes certain sense but if the code is public API then I think I can justify the preference of "full blown unit tests" (yes, even if "code is really that simple"). If you strongly feel otherwise, consider opening the dedicated question on that - this is a bit too "large" to clarify in comments – gnat Jan 11 '13 at 21:33
  • 1
    That is one of the most pointless tests I have seen. I don't see how that gains you anything other than saying it is "tested". – wobbily_col Mar 25 '15 at 09:16
  • @wobbily_col wait till the day you refactor it to change return true to something else – gnat Mar 25 '15 at 09:21
  • 1
    And why would you do that? Wouldn't you think to do a search for where that code is called from if you are changing it? – wobbily_col Mar 25 '15 at 09:29
  • of course I won't. Minimising (manual and error prone) search is primary purpose. I fall back to search only if failing test tells me about breaking assumption deemed important enough to state it in tes; otherwise I assume my changes okay and move along – gnat Mar 25 '15 at 09:44
  • 1
    Fair enough, but I really don't see much advantage on testing something this obvious. It just seems like testing for the sake of it. – wobbily_col Mar 25 '15 at 09:45
  • the very fact that code provides a value via non-private method suggests that it's not really this obvious – gnat Mar 25 '15 at 10:01
  • 2
    I gave you an upvote for the headline, but your example is more an example which shows some unit tests are pointless. I guess it could be improved by using a pseudocode function like AtLeastOneLineFunctionWithEnoughCodeToOverlookATypo(). – Doc Brown Dec 04 '15 at 08:56
  • @DocBrown you probably gave upvote by mistake then. Protecting complicated code is not my goal here, goal is protecting from possible changes in a simple code. I want test to be there just in case, eg to back me up if I decide to replace true with something like ComplexitySpecification.simpleEnough() etc – gnat Dec 04 '15 at 09:23
  • @gnat: that is a valid point of view, I do not deny that, and your answer is useful enough to deserve an upvote. However, I would give someone who has not written unit tests for his project so far (like the OP) a different recommendation which parts of his code should be equipped with unit tests first. – Doc Brown Dec 04 '15 at 09:32
114

I've never bought into the "you must unit test everything" idea, though there are certainly folks out there who have (see gnat's answer!).

As far as I'm concerned, the main benefits of unit testing are:

  1. Helping ensure changes don't break things.
  2. Helping you design sensible interfaces to your classes (since it forces you to be a client to your own code).
  3. Helping document how your code is expected to be used.

Basically you need to weigh the time it will take you to write & maintain tests against these factors.

Number 1 is usually sufficient to make it worth writing tests. In my experience, >95% of code gets modified sooner or later.

vaughandroid
  • 7,599
  • 9
    Agree with this - when working in a single developer shop you need to strike a balance between software quality and testing everything (which can take up a lot of your time). Don't forget that everytime you make a change you may need to modify your unit tests to suit – Matt Wilko Aug 08 '12 at 10:53
  • 1
    You shouldn't test everything, but you should test any behavior that's exposed to the outside world. There is/are certainly maintenance costs to testing, and as Kent Beck said in some SO answer (I think it was SO) - test enough to make you confident that your code is correct. – Wayne Werner Aug 08 '12 at 11:44
  • 11
    I'd add that if you're only doing minimal automated testing, unit tests are the wrong level to target. Instead go for high level integration/smoke tests that push a few data sets through your app end to end without mocking anything. You want these tests to exercise as much of your codebase as possible and cover all the normal use cases and execution paths. A 75% chance of knowing your changes broke something, somewhere, in the app is more beneficial than a 5% chance of knowing you broke SomeClass.OneOfTheHandfulOfTestedFunctions(). – Dan Is Fiddling By Firelight Aug 08 '12 at 12:30
  • 3
    Uh, I think you missed one: "Making sure the code works the way it's supposed to!" – BlueRaja - Danny Pflughoeft Aug 08 '12 at 16:40
  • 3
    @BlueRaja-DannyPflughoeft: Actually, it would be more accurate to say: "Making sure the code passes the unit tests you've written!" – vaughandroid Aug 09 '12 at 15:53
  • 1
    +1! In particular, I feel there is such a thing as testing too early. The web UI for our application has seen immense changes during initial development. We knew when it wasn't ready yet, and didn't bother writing tests for the various behaviours in it. It had bugs, yes, but that first draft of an interface and what we have in the end have nothing in common; all those unit tests would have been scrapped by now. Plus, testing web UI is never as simple as the currently top-voted answer shows. There are tradeoffs; don't pretend there aren't any and that "yes test" always wins. – Roman Starkov Jan 13 '13 at 11:42
36

It's simple: you don't need unit testing if you will discard the program after running it once.

If this seems excessive to you, consider what the alternative would mean. If there were some size below which unit testing doesn't pay, you would have to keep judging in your mind: "Have I reached the magic size yet? Should I start writing tests?" Now, programmers are notoriously bad at predicting the future, and they are notoriously bad at judging their own skill. Code that looks crystal-clear to you now will become incomprehensible, even to you, even by waiting a month. A project that you are absolutely, positively certain will never be used again, and that you keep around merely on the remote chance that you might want to look up how you solved something before, will be requested again, and will receive change requests.

It is therefore very likely that you will misjudge whether or not testing is helpful, and when you do start needing tests, you're already not 100% sure what the exact semantics to test actually are. Since I believe that all except trivial one-off programs profit from unit tests, I consider the cost of expending effort on tests that are never run again to be negligible against the risks of extending untested and ill-understood code.

Kilian Foth
  • 109,273
23

Some time ago I found a nice post: Why unit testing speeds up development. It can help you answer the question.

...What if the codebase... was provided with a substantial set of unit tests. A set that would say : “if all tests succeed, I guarantee that code still does what it should do” and if a test fails it exactly shows you where some behavior is broken . This is great, I could change the code to add things I want without wandering if the code still does what it should do, I just run the... tests and I have faith. This is a great world to be in as a software engineer. I noticed that I could advance way faster...

I have “faith”.

This is probably the most important reason why unit tests speed up development in a enterprise context.

I can trust that everything still works if all tests pass. If tests fail they will point out exactly where the problem lays...

...you have to have a high unit test coverage and good unit tests. When these conditions are respected you’ll find yourself in a situation that the effort for adding new functionality ‘s is almost independent of the size of the application and that in the long run it will speed up development.

Michael Feathers introduced in one of his books two ways of working with changes in code:

  • edit and pray,
  • cover and modify.

It does not matter how big is the code base.

gnat
  • 21,213
  • 29
  • 113
  • 291
20

According to Kent Beck in his answer to Stack Overflow question How deep are your unit tests?, you should test the code that you tend to get wrong.

If I don't typically make a kind of mistake (like setting the wrong variables in a constructor), I don't test for it.

Mansuro
  • 341
  • 4
    Good point, and this implies that the OP's question is wrong; whether to test has nothing to do with the size of the project. A 5-line codebase may need tests, and a 1-line method in a large codebase may not (though other things in that codebase do). – Nathan Long Aug 08 '12 at 16:36
  • This may work for tiny projects that you're working on alone, but for a project that is either done for work (where you don't control who will work on it in the future) or anything that may be open source, you need to test everything. And you need to start immediately because later it will be "too hard to backfill all the code that's already written" – xaxxon Jan 12 '13 at 17:53
  • @xaxxon: Kent actually talks about that in the same answer that Mansuro linked to, saying: "When coding on a team, I modify my strategy to carefully test code that we, collectively, tend to get wrong." – henko Jan 12 '13 at 19:47
  • No, that's still incredibly short sighted. You cannot know who will be working on the code in the future. This is the type of mindset that afflicts junior developers. – xaxxon Jan 13 '13 at 03:21
5

Unit tests are implemented to save time and improve:

  • bug tracking
  • refactoring and/or re-writing code
  • integration testing
  • regression testing, etc.

It is a must to write unit tests for relatively complex parts of the program.

If you are sure that writing unit tests now will not save your time in future, you can skip it. However, you never know, and personally I can't think of a case when it is cheaper (in sense of time, money and nerves) not to write unit tests but do all the testing manually instead.

superM
  • 7,383
  • Generally a good answer, so +1. However, I think your final point misses the fact that you'll still want/need to manually test your unit tests! – vaughandroid Aug 08 '12 at 09:16
  • @Baqueta, maybe I didn't sound clear, but I didn't mean say that unit tests completely replace the manual testing – superM Aug 08 '12 at 09:31
5

Unless you are going to write code without testing it, you are always going to incur the cost of testing.

The difference between having unit tests and not having them is the difference between the cost of writing the test and the cost of running it compared to the cost of testing by hand.

If the cost of writing a unit test is 2 minutes and the cost of running the unit test is practically 0, but the cost of manually testing the code is 1 minute, then you break even when you have run the test twice.


For many years I was under the misapprehension that I didn't have enough time to write unit tests for my code. When I did write tests, they were bloated, heavy things which only encouraged me to think that I should only ever write unit tests when I knew they were needed.

Recently I've been encouraged to use Test Driven Development and I found it to be a complete revelation. I'm now firmly convinced that I don't have the time not to write unit-tests.

In my experience, by developing with testing in mind you end up with cleaner interfaces, more focussed classes & modules and generally more SOLID, testable code.

Every time I work with legacy code which doesn't have unit tests and I have to manually test something, I keep thinking "this would be so much quicker if this code already had unit tests". Every time I have to try and add unit test functionality to code with high coupling, I keep thinking "this would be so much easier if it had been written in a de-coupled way".


TL;DR version:

Write a test when the cost of writing the test, plus the cost of running it as many times as you need to is likely to be less than the cost of manually testing it as many times as you need to.

Remember though that if you use TDD, the cost of writing tests is likely to come down as you get better at it, and unless the code is absolutely trivial, you will probably end up running your tests more often than you expect.

Mark Booth
  • 14,302
4

Test as soon as you observe regressions happen or fear causing some with your edits and not noticing.

Kindle that fear, let it grow to an adequate size: the sooner you test, the better.

Please note: depending on your role in the project, unit tests may not be the only kind of tests you want to write.

Everybody is rightfully obsessed with unit tests, because of bad mainstream testing practices in the past; but if you never tested before, you really should focus on testing in general, unit testing alone isn't going to solve world's problems.

ZJR
  • 6,331
  • 1
    I see you point but isnt't unit testing the basic starting point for startup programmers. And also could you elaborate more on what you mean by "testing in general". Thanks. – Lamin Sanneh Aug 08 '12 at 08:27
  • 1
    There are at least two other types of testing - integration test, and acceptance tests. Unit tests cover a specific unit (e.g. this function of this class is supposed to Frab the Fizz). You mock/stub all the other places where that class interacts, passing it el fake-o data. When you do integration tests, you combine two (or more) classes to make sure that your classes go together the way you think they should. Eventually you build up enough tests that you're doing end-to-end testing of your entire system, sometimes known as "smoke tests". – Wayne Werner Aug 08 '12 at 19:33
  • There's also regression testing tout court. Regression tests are usually bigger than unit test, may take a day to build, may imply access to test data and special test environments. Actually the unit tests are a smaller, a sleeker, and a more maintainable version of (old-style) regression tests. – ZJR Aug 09 '12 at 00:47
4

"Should I unit test this" can usually be answered by answering the following question: "Is it important that this function works properly, and is it important for me to know when it stops working?"

Of course, it's a lot more complicated than that, but that's a good way to start. Eventually you'll also weigh whether the code is already being tested by virtue of being used in another function, or whether your time would be better spent in another function, etc.

Bryan Oakley
  • 25,332
2

I believe that it's not the size of the project but the type of project which decides if it should use testing or not.

If you are working on a proof of concept, or on some other project where the goal is to learn from the coding, then testing is not required. If the project is meant to be used, maybe even sent into production, then it should be tested.

A quote from Robert C. Martin's "Clean Code": "If it's trivial to write, it's trivial to test", so there's no excuse to skip on the testing for short and trivial programs.

0

It really isn't a matter of size-- it's what you do with it (and how old it is). If I'm noodling around learning how a technology works and plan on throwing the thing away (we call this a spike in Scrum), I'll just code without doing much testing. If you are planning on developing it further (even if you are just noodling around), you should write tests around the code. TDD is a design technique even before it is a testing technique. You want to have a clear picture of what you want the code to do before you get bound up in the particulars of the execution. I would also NOT recommend trying to write extensive unit tests around large amounts of legacy code. Try to identify those parts that are complex (have a high cyclomatic complexity/spaghetti code feel to them) and those parts which fail frequently (they will often be the same). As others have suggested, I'd go read Kent Beck's book on TDD and definitely read Michael Feather's book. What they don't cover very much is the political aspect to writing unit tests around code. A lot of developers hate having to alter their code to make it testable, and a lot of developers do not feel the need to test code at all. It is something we have to work on as a profession. Every other engineering discipline is required to prove (often mathematically) that their work met the specification. We should do the same.

-1

Binding a Projects in limits of classes or functionality and then judging if this is suitable for unit testing may be wrong. There are numerous benefits of unit testing an application but real beauty starts when you have to maintain an application or have to work in distributed environment. So the choice comes here. Even a small change in your code can cause disasters.
I would not only suggest 'Unit Testing' but would also recommend to check your code coverage, ensuring maximum of your code is covered with Unit Test. Threshold for code coverage shall not be less than 95% and target must be around 100%. You can go for tools to track your code coverage and generate a report.
Visual Studio Provides Coverage Data - http://msdn.microsoft.com/en-us/library/ms182496(v=vs.80).aspx
Also you can use NCover or dotCover.

Niks
  • 1