I Pity The Fool Who Doesn't Write Unit Tests

@Zeo

On a smilar note, however, I do think that unit tests are kind of
waste of time.

Pretty much everyone does until they actually try them.

From what I have read and understood, it seems more like unit
tests as a tests which one can do while they code any way.

Yes. But unit tests are repeatable, very quickly. You stepping through a debugger is both error-prone and slow.

writing more code specificaly for tests for situations I KNOW
for a fact that will change will eventually require change
in test modules as wellā€¦then why bother?

Thatā€™s the most important reason to write unit tests. You will find that the code being tested changes much more frequently than the unit tests. Having automated tests makes it far quicker to test, and allows far more confidence in the changes youā€™ve made.

But as I said I will have a go at them.

Good :slight_smile: Itā€™s the single most effective thing you can do to improve your productivity. It is much quicker to write unit tested software than not (partly because of the vastly reduced amount of bug fixing you do, partly because you tend to end up with a better, more maintainable design)

@Damien

TDD creates all this inertia around the initial interfaces in the
code, which start to affect the design. Or at least I think it
does, Iā€™ve never actually tried it.

Ah, arguing from ignorance :slight_smile: The amount of debate over practices like this from people who have never actually done it is astounding :slight_smile:

who has time to try out every amazing new methodology?.

TDD is not a technology, but a way of doing things.

Iā€™m with Alyosha. 100%. Unit tests are for beginners. Beyond that theyā€™re a monumental waste of time and effort.

Actually, this comment has it 100% wrong. Itā€™s the beginners who donā€™t get unit tests. They may have been coding for years, but theyā€™re still beginners (in the Dreyfus model sense of the word).

You see this in two ways:

  1. Unit test coverage should be 100%. You hear this from recent converts mostly, but itā€™s not true. Unit tests are very weak in some areas. I never unit test my UI or report layouts, for example. Itā€™s possible, but there are better tools to test those things.

Eventually, (some) people get to recognise when unit testing is appropriate and when itā€™s not.

  1. Unit tests are a waste of time. You hear this mostly from people whoā€™ve never tried it, or havenā€™t tried it much. In fact, if you listen to people who are experienced at unit testing, they are pretty much all agreed that writing code is quicker if you unit test it, and particularly so if you write the tests first. Furthermore, the benefits accrue over the years you have to maintain the code you just wrote.

Unit testing tests your codeā€™s behaviour explicitly with little coloured lights, and its design implicitly, because writing tests for bad architectures is hard. Write the tests first and you test your design upfront and continually.

Writing code with fewer bugs and a better design saves you time and money both initially, and over the years when maintenance is easier and more reliable.

If you are not already doing it, then adopting unit testing is the single best thing you can do to improve your productivity. Itā€™s not a silver bullet, but it most certainly is a best practice. Just donā€™t try and do it for every single thing.

you are very intelligent guy can you teach me some thing more about civics i am a 12 year girl i am studieng in 6th std

ā€œany tests are better than zero testsā€

I disagree; like comments, incorrect ones are actually worse than none at all.

Not a fan of TDD myself. My arguments against unit testing are:

  1. Unit tests only prove that the code works for the specific inputs/outputs that the programmer has already considered. For experienced developers, virtually all bugs occur in the edge cases that arenā€™t covered by unit tests.

  2. Unit tests really do take a lot of time to write and maintain if done properly, and in informal projects or any project where the requirements change, itā€™s easy to spend over half oneā€™s time just keeping the tests up to date. Sometimes you know that youā€™re going to break old code, and then in addition to changing all the old code, you now have to change all your unit tests (or just give up on the unit testing for that code, which is often what happens in practice).

  3. Unit tests do a very poor job when it comes to threading and timers, for reasons that should be self-evident.

  4. Unit tests are mediocre for anything database-driven, where often the only way to verify correctness is to use production data and verify the results (itā€™s possible to do this in a full-fledged test environment, but not in a unit test).

  5. Unit tests do nothing to expose architectural flaws, which are the most expensive ones to actually fix (assuming the requirements are correctly defined). Those flaws are generally discovered during the coding process, which is obviously going to move slower if one is also dealing with unit tests, and that means the time it takes to discover such defects is greater overall.

  6. In my experience, at least 1/3 of the bugs are introduced in the UI, which unit tests can never test properly. Maybe some day computers will be able to model the chaos that humans bring into the fray, but weā€™re not there yet.

Given time I could probably think of 4 more points, but I think thatā€™s enough for now. :slight_smile:

My biggest beef with McConnell (Code Complete) is his religious fervour for unit tests. As other people have commented, and as James Bach has famously stated, there are no best practices, and unit tests are too often treated as a best practice. For extremely large projects that have massive APIs which are all supposed to work in deterministic ways, unit tests can be a great tool both for initial testing and regression testing. For anything smaller (Iā€™d say anything 100,000 lines or less), less formal, data-driven, etc., they may merely create the illusion of working code and are not always such a useful practice.

I have yet to see any truly persuasive argument that unit testing is a best practice, or even a good practice, in the general case. Having said that, if they help someone write code more efficiently/accurately, then I would not by any means try to steer that person against unit testing. People should use whichever tools they find useful; I just wish they wouldnā€™t prescribe their elixirs to everyone. If I were managing a team, I would not try to stop any team members from using unit tests, but I would not force the team to use them either. Code reviews and -actual- testing (by a good tester) will reveal deficiencies much faster and with better accuracy.

Sorry for the rant Jeff, youā€™re usually spot-on (after all, thatā€™s why Iā€™m here) but I think this one might have missed the mark. Canā€™t win 'em all though!

Unit Tests before you write code only works if the design of the code has been done up front. Unfortunately, I think this is rarely the case. I think most developers donā€™t know how things are going to work out until they start writing code. Now, whether that is a good or bad thing can be debated. But its hard to write tests if no design exists.

Ideally, the design of the application would be known so that technical testers could write the test harnesses to fully test the application. Ideally, you would want a structural test of the code (unit test) followed by a functional test of the code. The structural testing could be done by something like NUnit. Functional testing is having users actually step through the system and verify functionality.

Unit tests are valuable, provided both positive and negative test cases are supplied. Many bugs can be found during unit testing. Keep in mind during unit testing you are not only testing your code, but the numerous libraries you are employing across the application as well.

I guess my only conceern is that it compiles. Believe it or not, I have had cases where checked in code did not compile, so unit testing may be asking a bit much in any shape or form.

Reading the subsequent comments, I think the most common response to my arguments was ā€œyes, but so what?ā€ and the second most common was ā€œnuh uh!ā€ followed by a set of further utopian best-practices (such as ā€œisolate the UI from the business logicā€).

Thereā€™s a fundamental misunderstanding of the argument here. I said from the get-go that unit tests can be useful and I donā€™t have anything against them. My problem is with those who:
a) Believe that theyā€™re a legitimate substitute for actual design and documentation
b) Honestly think that they can test for every conceivable bug
c) Recommend - nae, INSIST on it as a ā€œbest practiceā€.

Each of these lines of reasoning is not only sloppy, but dangerous.

(a)
If your program is anything but a throwaway or one-off, then thereā€™s no excuse for hacking it out without a detailed requirements document and design/functional spec. Iā€™ve already spotted a few ninja-style programmers in the comments who ā€œjust wanna codeā€ and use ā€œbut I have unit tests!ā€ as an excuse to prove that theyā€™ve done it correctly even though thereā€™s no supporting documentation. This attitude will burn you in the end. Some people adore unit tests precisely because they are simply code, and believe theyā€™ve discovered a way to avoid the mind-numbing work of documenting things. This is patently wrong. Unit tests arenā€™t understood by your customers, and they wonā€™t help a maintenance programmer much either (I know from experience).

(b)
Unit tests may prove that a particular class or method works, but contrary to popular belief, it does not -always- prove that it works correctly. As Jonathan Allen pointed out, you are testing the part of your program that is not only least likely to fail, but also the easiest to debug later on and the least expensive to fix. The real problems, the most difficult ones to fix, are the design flaws. Interdependencies between routines and classes. Semantic mistakes. Sequential coupling. Heisenbugs/schroedinbugs. Erratic behaviour under low-memory conditions. Strange errors getting propagated from library (i.e. DB or networking) code due to things like poor networking conditions. Unit tests test help with precisely 0% of these. If design problems account for 80% of the debugging (Iā€™d say thatā€™s a conservative estimate) then even if unit tests halved the time spent on code problems, itā€™s still only saved you 10%, and that may very well be offset by the time spent creating and maintaining the unit tests.

Ā©
Unit testing is just one tool that can be used in a full QA program. If the people who preach Unit Tests got half as excited about other types of QA (i.e. exploratory testing and code reviews) then I might be more inclined to listen to them, but instead I get the very strong feeling that they consider it an adequate substitute for a full QA program, which it isnā€™t. There are no best practices. Learn how to do your job, which means knowing precisely how much or how little testing your project requires, and what the best test methods really are. Donā€™t just assume that unit tests will magically solve all your problems.

I have a real problem with Unit Testing gospel because Iā€™ve seen the above three points in practice. In fact, these assumptions are implicit in many development and consulting shops who claim to use ā€œAgileā€ methodology. I note that true Agile development can be extremely effective but I am referring here to the vast hordes who use Agile/XP as a buzzword. Iā€™ve actually seen people state that they donā€™t need documentation and that Unit Tests are just fine for a complete QA program. Iā€™ve seen the result of this thinking in practice. Itā€™s always a disaster. The resulting product never works correctly. The evidence usually seems to indicate that the people who made it donā€™t even completely understand what itā€™s supposed to do - they just dove into the code without thinking and did what THEY wanted to do without heed for actual requirements. Ask for documentation and months may go by before you receive anything. Serious errors are fixed slowly. Enhancements, if implemented at all, can take ages.

Bottom line: Unit Tests are often good. Unit Testing Evangelism is always bad.

The 12 benefits are mostly correct, but listing the benefits without the drawbacks is like thinking about the benefits of buying a house, changing jobs, or marrying someone without also considering the drawbacks or advantages of not doing so. For the sake of getting us to think in different ways, I am going to list the 12 drawbacks or opposing thought of the combination of unit testing and TDD that are roughly equivalent to these 12 benefits. Warning, some of these are kind of the same, as the initial 12 are mostly based on the same underlying idea.

  1. TDD forces you to prove your code does what you think it should do before you are in a position to know what it should do.
  2. TDD makes it very difficult to refactor, because then you have to refactor all your tests with your code. While techniques for limiting the complexity and length of the code itself are readily available, this is much more difficult to do with unit tests. If you are using TDD and you donā€™t have way more unit test code than actual code, you are either not very good at thinking of ways your code could be wrong, or you are not very good at simplifying the code under test.
  3. You cannot improve the design of your code without breaking all that unit testing code.
  4. Itā€™s more fun to be fast and flexible and write lots of functionality with a small amount of code and than to be constantly bogged down by unit testing everything.
  5. They trick you into a sense of false progress.
  6. Unit tests provide mostly uninteresting samples of your code that makes people wonder why you even bothered to make that class to begin with. Real world samples of your code already exist in the code base which will provide much more motivation to learn and use them.
  7. It forces you to make a detailed plan before you know what you are trying to achieve.
  8. It forces the cost of bugs to apply to your initial designs that will never be used anyway. It increases the cost of all your non-buggy code to be the same as buggy code.
  9. Code inspections allow for the combining of two minds and the passing on of knowledge and design principals.
  10. It encourages flow, which is a dangerous thing. It prevents you from questioning yourself and encourages you waste hours going down rabbit holes.
  11. TDD makes impractical designs that have to be changed even before they are actually used.
  12. Itā€™s slower than writing code without tests.

To give you some background, I do use unit testing in my current projects. I used to use them a lot more, and I used to do test driven development. But now I basically only use it for very complex algorithms that can be moved into general purpose, reusable, testable pieces of code. Even then, if doing so actually changes it into a bunch of not so complicated algorithms that donā€™t need testing, then Iā€™m not really sure how useful the unit testing or the abstractions are, unless you see it will be reused a lot, and simplify other code. Even then, it might be better not to have the unit tests. I am all for extremely powerful, reusable, abstractions, but not so much for turning everything into a not so powerful one. The powerful ones I create are so powerful, most developers would never even think of them and skyrocket my productivity and amount of code reuse at times. In the case that, and by the time I am ready to write unit tests, the interface to my class is already designed, but I do write the tests before the implementation, and this does influence the internal design to some extent, for better or for worse. But I do think it is good to write a few different failing tests before doing any implementation. Not that you should write all the tests first, but at least use the class in a few different ways, so you are not intentionally implementing it overly simplified just to make the first tests pass and then have to change it later. Write enough tests first that you can start to think about how the algorithm is going to end up, and try to create an implementation that will make them all pass. If you get bogged down trying to get them to pass, only then should you revert to oversimplifying the algorithm to make at least one test pass and then move forward from there. After all those tests are passing, then you can add a few tests at a time, each group of new tests should illustrate a different way of using the object in a way that does not encourage you to oversimplify any required algorithm adjustments. When you think you have a complete and working implementation, but feel more tests are necessary to find or prevent bugs, only then should you add one test at at time, and run each one right after writing it. I find that rather than to use TDD to create the interface / external design, I like to pretend any class I want magically already exists and is implemented perfectly, and I just use it how I wish I could. Then when the code using it seems complete, I know what interface I actually want for those non-existent classes. This prevents me from wasting time testing and implementing something that will change before it ever really gets used. I am not opposed to the possibility that I could be wrong, but I have read a LOT of things about the advantages of unit testing, and I understand them well. The concepts were so convincing that I immediately adopted them and felt they couldnā€™t possibly be wrong. Over time, my experience has greatly changed my viewpoints.

1 Like

Hi still_dreaming_1. I understand the frustration you might have in thinking of the additional work involved in developing unit tests. Believe me I know considering I constantly get roped into poorly written architectures which either violates or excludes every programming principle. Unit testing didnā€™t work in my situations since primarily the architecture was tightly coupled to a data source so immutable data sources are impossible if you have spaghetti code which calls the database almost constantly in the business logic. But consider this before you completely write off unit testing. With the growing popularity of NoSQL frameworks, EF and repository patterns (Iā€™m using MongoDB currently in my personal solutino) all of those problems are a cinch to solve. It may also be the architecture that you choose to build on which primarily bars you if SRP and decoupling is not achieved which is the added headache. But for the most part, if you setup an API architecture properly using solid principles and is well designed then you just need to unit test the BL. Also if your like me and choose an N-tier you only need to unit test those (like you mentioned) are complex algorithms or business logic. For the longest time it seemed impossible without some sort of IoC container if you are doing an enterprise service bus but after carefully considering the generics it becomes extremely simple with frameworks like moq ect.

It is also worth mentioning that repetitive testing in web development is most likely to become extinct with frameworks like Selenium. Lastly if you ever ask yourself after you saw the same screen for the n^n time during testing ā€œthere has to be an automatic way to regression testā€ then TDD (unit testing+integration testing+UI) might be for you. If you donā€™t ask yourself that then by all means continue manual regression tests at your leisure. You may find enjoyment out of it as I know some developers do :smile: