20 ms·
The tragedy of 100% code coverage (2016)
- brlewis 9y agoThere's a human tendency to overemphasize things you can quantify. So we try to figure out how to test every code path rather than what we should do: try to figure out which inputs we should test against.
- deleted 9y ago[deleted]
- ioquatix 9y agoI have 100% code coverage on a couple of projects. It has two benefits: Behaviour is completely covered by tests, so changes in APIs which might break consumers of the library will at least be detected. New work on the library tends to follow the 100% coverage by convention, so it's somewhat easier to maintain. Apps that have 90% coverage, for example, tend to slip and slide around. Having 100% coverage projects the standard "If your contribution doesn't have 100% coverage it won't be accepted". I don't think this is a bad default position.
- quantumhobbit 9y agoThe problem is that coverage tools report whether that line of code executed and not whether its logic is correct. This can easily give you a false sense of security. So if I want to contribute to your project all I have to do is write some pointless tests that are sure to execute every single getter and setter method.(yes I have seen tests that exist solely to execute getters and setters). I don't have to actually test any known edge cases.
- enraged_camel 9y agoExactly. It's like testing a newly built bridge by crossing it a hundred times with a bike. Sure, you "covered" it, but you sure as hell did not test it.
- ben_jones 9y agoA good example would be in dynamically typed languages where you can hit a code path but forget to validate a certain input type which could cause a bug. Or one I've been personally hating lately, a nil pointer dereference in Golang from a well tested library that didn't do proper error propagation.
- tyingq 9y agoYep. Intel's AMT bug was exactly that...unexpected input, but in a static typed language. And would not have been discovered with 100% code coverage.
- ademarre 9y ago> a false sense of security Yes. Code coverage should not be the primary measure of quality of your tests. That thinking leads to tests designed to cover lines, not use cases.
- defined 9y ago> The problem is that coverage tools report whether that line of code executed and not whether its logic is correct. This can easily give you a false sense of security. This is true. But isn't that true of any test? A suite of naive or badly written tests can also give us a false sense of security, so why write any at all (I don't mean that literally)? I think that a greater level of confidence in our code can be achieved by a combination of - Judicious choices of unit and integration tests - Static and dynamic analysis (if the language supports it), and - Property-based testing (the canonical example of which is QuickCheck[1]). Property-based testing is a great way to help us hit those edge cases. As for 100% code coverage, I think it is worth striving for, not just for the sake of having all lines of code tested. It can expose design and testability flaws, for example. If 100% coverage cannot be achieved, we need to ask ourselves: - Did we really need that piece of code we couldn't test? Is it actually called anywhere, or is it one of those YAGNI things? - Did we write code that is not very testable? Are there functions or methods, for example, that are so dependent on external state that they can't be mocked or tested some other way? Should we refactor it? - If it's a trivial line of code like a getter or setter that cannot possibly be wrong, then it should be fairly trivial to generate automatically a test case for it. More severe defects have probably been caused by a single line of untested code[2][3] than we may suspect. - If it's a getter or setter, and it's not used (and therefore code covered) in other test cases, maybe it's superfluous and should be removed. References [1]: https://en.m.wikipedia.org/wiki/QuickCheck https://en.m.wikipedia.org/wiki/QuickCheck [2]: https://www.imperialviolet.org/2014/02/22/applebug.html https://www.imperialviolet.org/2014/02/22/applebug.html [3]: http://users.csc.calpoly.edu/~jdalbey/SWE/Papers/att_collapse.html http://users.csc.calpoly.edu/~jdalbey/SWE/Papers/att_collaps...
- saltedmd5 9y ago> If it's a trivial line of code like a getter or setter that cannot possibly be wrong, then it should be fairly trivial to generate automatically a test case for it. More importantly, if it's a getter or setter, the actual behaviour to be tested is probably broader than "should set/get x." Test scenarios, not methods.
- frik 9y agoExactly. It's all about logic. They other types of bugs are caught by the IDE, the compiler or the first run anyway. Let someone else look over your code, they might spot logic bugs that you oversee. If you write tests yourself, you will often oversee such logical bugs. I have done (semi-)automatic reasoning and code verification with 100% code coverage of OS drivers. All the mathematical reasoning doesn't spot logical errors. You need more than one person to look over it.
- FroshKiller 9y agoA "pointless" test of a getter or setter could save you a lot of trouble later after someone introduces a side effect.
- saltedmd5 9y agoThis is completely true, but it's an argument against using coverage as a metric for test quality, not against test coverage per se.
- ioquatix 9y agoNope, if you submit a PR with crappy unit tests, I'll throw it politely back in your face and tell you to rewrite them :D
- djtriptych 9y agoMy version of this is working on a team with 100% coverage that still saw a steady and heavy influx of bugs. 100% coverage does not mean bug free. I advocate spending time on identifying/inventing the correct abstractions over coverage.
- zxcmx 9y agoI wish more people cared about path coverage as opposed to "line coverage".
- flukus 9y agoIt's slightly more meaningful in the right hands, but still meaningless in the wrong ones. You can get 100% line coverage and never have a single assert.
- ajmurmann 9y ago100% code coverage and even TDD'd doesn't and shouldn't mean 100% unit tested. Glue code and declaration doesn't need a unit test. Some functional tests should provide all the coverage needed to give you confidence to refractor that code in the future. Edit: while I'm a huge TDD advocate, I'm not a big advocate of measuring code coverage. That should only be necessary if you are trying to get a code base under coverage that wasn't TDD'd. Even then I'd rather add the coverage as I'm touching uncovered code. If it works and I'm not touching it, it doesn't need tests.
- hibikir 9y agoI might be completely wrong on this one, but it seems to me that a lot of the precepts of TDD and full code coverage have a lot to do with the tools that were used by some of the people that popularized this. Some of my day involves writing Ruby. I find using Ruby without 100% code coverage to be like handling a loaded gun: I can track many outages to things as silly as a typo in an error handling branch that went untested. A single execution isn't even enough for me: I need a whole lot of testing on most of the code to be comfortable. When I write Scala at work instead, I test algorithms, but a big percentage of my code is untested, and it all feels fine, because while not every piece of code that compiles works, the kind of bugs that I worry about are far smaller, especially if my code is type heavy, instead of building Map[String,Map[String,Int]] or anything like that. 100% code coverage in Scala rarely feels as valuable as in Ruby. Also different styles make the value of having tests as a way to try to force good factoring changes by language and paradigm. Most functional Scala doesn't really need redesigning to make it easy to test: Functions without side effects are easy, and are easier to refactor. A deep Ruby inheritance tree with some unnecessary monkey patching just demands testing in comparison, and writing the tests themselves forces better design. The author's code is Java, and there 95% of the reason for testing that isn't purely based on business requirements comes from runtime dependency injection systems that want you to put mutability everywhere. Those are reasons why 100% code coverage can still sell in a Java shop (I sure worked in some that used too many of the frameworks popular in the 00s), but in practice, there's many cases where the cost of the test is higher than the possible reward. So if you ask me, whether 100% code coverage is a good idea or not depends a whole lot on your other tooling, and I think we should be moving towards situations where we want to write fewer tests.
- ben_jones 9y agoIMO ruby is the worst because its so easy, and often times encouraged, to write layers of useless tests that ultimately give only a false sense of security. I recently re-did the Michael Hartl tutorial to catch up with Rails5 and it gives examples where you test proper template rendering. I love that book but I'd rather shoot myself in the foot then spend dev time writing tests to check if the right html title attribute was rendered for the page...
- mjevans 9y agoI think I'd rather focus on documenting the information flow. Of having the tools to track down where things start to go wrong when there's a problem and I ask things to run with more verbosity. Initial "complete coverage" should probably start from mockups that test an entire API. The complete part should be that, in some way, the tests cover expected successes AND failures (successfully return failure) of every part of the API, but there's no need to test things individually if they've already been tested by other test cases. Invariably reality will come up with more cases and someone will notice an area that wasn't quite fully tested. That's where a bug exists, but the golden test cases probably wouldn't have located it anyway. It'll take thousands or millions of users to hit that combination and notice it. Then you get to add another test case while you're fixing the problem.
- kabdib 9y agoI've seen projects where management had rules like "you must have 70% code coverage before you check in". Which is crazy, for a lot of reasons. But the developer response in a couple cases was to puff the code up with layers of fluff that just added levels of abstraction that just passed stuff down to the next layer, unchanged, with a bunch of parameter checking at each new level. This had the effect of adding a bunch of code with no chance of failure, artificially increasing the amount of code covered by the tests (which, by the way, were bullshit). I got to rip all that junk out. It ran faster, was easier to understand and maintain, and I made sure I never, ever worked with the people who wrote that stuff.
- coding123 9y agoI wish people cared more about the craft of an amazing plugin architecture or an advanced integration between a machine learning system and a UI, but no, more and more of our collective development departments care more about TDD and making sure things look perfect. Don't worry about the fact that there are no integration tests and we keep breaking larger systems, and while there might be 100% code coverage, no developer actually understands the overall system.
- borplk 9y agoYou may find a long rant I wrote recently interesting. https://news.ycombinator.com/item?id=14285926 https://news.ycombinator.com/item?id=14285926
- Clubber 9y agoYes, unit tests have their place, but you have to understand why they are needed. To me, it's secondary and has tones of bike shedding in it. Writing tests is easy, mentally. Getting a good, simple, YAGNI/DRY architecture is more challenging and requires several iterations, something that is resisted if you have to rewrite all your unit tests. Let me put it this way, if you write an architecture that you later don't like, you would be more hesitant to scrap it and start over because all the additional work of the unit tests (especially if you're on a deadline). That's bad. To write a good architecture, a developer must be willing to realize he could have done it better and be willing to tear it down instead of build around the bad architecture. That's how you write 20 year code, which should be almost everyone's goal.
- penpapersw 9y agoI think a bigger epidemic is we're putting too much emphasis on "do this" and "do that" and "if you don't do this then you're a terrible programmer". While that sometimes may be true, much more importantly is to have competent, properly trained professionals, who can reason and think critically about what they're doing, and who have a few years of experience doing this under their belt. Just like other skilled trades, there's a certain kind of knowledge that you can't just explain or distill into a set of rules, you have to just know it. And I see that in the first example in this article, where the junior programmer is writing terrible tests because he just doesn't know why they're bad tests (yet).
- devrandomguy 9y agoIt seems to me that management is taught that a dependence on expensive experts, is a problem to be optimized. They want to manage the development process in a way that allows them to easily swap out one developer or team for another, or to ramp up production simply by increasing the number of developers assigned to a task. It is almost as if they see the success that we have had in dev/ops with the "cattle, not pets" philosophy, and want to apply that in their own field. Making the subject behave consistently and predictably, whether it is a machine or a human professional, would be a prerequisite for that.
- ruleabidinguser 9y agoWill they succeed?
- devrandomguy 9y agoSure, as long as their users unanimously abide by a very large rulebook ;)
- penpapersw 9y agoI'd say they are "succeeding" in the sense that they continue to make money. Whether they're truly successful in the sense of providing proper value to their customers to justify that money, and treating their programmers right, is another story, and I've seen enough cases where they don't do either of these, while the executives make plenty of money to live comfortably for years by exploiting both customers and their own programmers.
- cbanek 9y agoI've had to work on mission critical projects with 100% code coverage (or people striving for it). The real tragedy isn't mentioned though - even if you do all the work, and cover every line in a test, unless you cover 100% of your underlying dependencies, and cover all your inputs, you're still not covering all the cases. Just because you ran a function or ran a line doesn't mean it will work for the range of inputs you are allowing. If your function that you are running coverage on calls into the OS or a dependency, you also have to be ready for whatever that might return. Therefore you can't tell if your code is right just by having run it. Worse, you might be lulled into a false sense of security by saying it works because that line is "covered by testing". The real answer is to be smart, pick the right kind of testing at the right level to get the most bang for your buck. Unit test your complex logic. Stress test your locking, threading, perf, and io. Integration test your services.
- mcv 9y agoIf you absolutely want to cover all cases, you need to do mutation testing. A mutation testing system analyses your code, changes an operator (> becomes < or >= for example), and then runs your tests. If one test fails, your tests covered that statement. It seems to me you need to have serious OCD to go for 100% mutation coverage, but that is what you really need to do if perfect coverage is your aim. The other option is to accept that perfect coverage is not feasible, or possibly even a trap, and that you just need to cover the interesting bits.
- jacques_chester 9y agoStrictly, mutation testing can't assure all cases either -- combinatorial state explosions are fun. But it does improve assurance a lot.
- MereInterest 9y agoHow would automated mutation testing handle the case of accidentally causing infinite loops, or invoking undefined behavior?
- 9y ago
- dcw303 9y agoI think you should write a test. Naming the test just "initialise" is not very useful as it doesn't assert what you expect the method under test to do. Given that the purpose of the initialise function is to populate a watchlists collection variable from the parameter, i'd name the test something like "initialise_daoRecordCountIs9_watchlistCountIs9". The pattern I generally use is <method_name>_<assertion_under_test>_<expected_result>. Then, my test would be the following: * Set up / mock the dao parameter to have 9 rows * Create an instance of the class under test and push in the dao parameter * Verify / Assert that the class under test now has 9 items in the watchlists variable - I'm assuming there is a public method to access that.
- DTrejo 9y agoHas anyone seen a fuzzer that creates variants based on a test suite with 100% coverage? Hmm... the fuzzer still wouldn't necessarily know how to create the correct invariants. #lazyweb
- mikestew 9y agoThe tragedy of 100% code coverage is that it's a poor ROI. One of things that stuck with me going on twenty years later is something from an IBM study that said 70% is where the biggest bang-for-the-buck is. Now maybe you might convince me that something like Ruby needs 100% coverage, and I'd agree with you since some typing errors (for example) are only going to come up at runtime. But a compiled (for some definition of "compiled") language? Meh, you don't need to check every use of a variable at runtime to make sure the data types didn't go haywire. The real Real Tragedy of 100% coverage is the number of shops who think they're done testing when they hit 100%. I've heard words to that effect out of the mouth of a test manager at Microsoft, as one example. No, code coverage is a metric, not the metric. Code coverage doesn't catch the bugs caused by the code you didn't write but should have, for example. Merely executing code is a simplistic test at best.
- ruleabidinguser 9y ago70% coverage... I don't know if I'm going to be able to give up my 0% coverage 1-man projects...
- BigJono 9y agoI'd recommend aiming for 10-20% on those projects, and also for startups trying to rapidly push an MVP out. Tests have diminishing returns. You want to hit the absolute most crucial ones that give you plenty of bang for buck and even save you time. That means finding the (usually small handful) of functions that implement your most crucial and most complicated business logic, and writing tests for them. Anything past that is for companies with customers that need to maintain a certain level of quality and service. Worry about it when you get there.
- dvirsky 9y agoAgreed. If you're a small start-up in a hurry and <4 devs, the best strategy would be to write top-down smoke tests on your API that will actually tell you when things break, even if not exactly where. Their actual derived coverage might be pretty high.
- kelnos 9y agoI find that, as I'm building something from scratch, the vast majority of the errors I make are just things I didn't think of. Tests don't help there because I can't test on input that I don't even imagine happening. So I generally write few tests, because, to be honest, most code is trivial and algorithm-light. Sure, if I have to write a parser or something a bit more fiddly, I'll write a unit test to be sure that it's doing what I expect, but that tends to be the exception, not the rule. I do write my code with an eye toward later testability if it turns out to be necessary, but I find that to be fairly easy, and also a good measure of if I'm doing the write thing: most code that isn't testable is probably code that's difficult to read and maintain, anyway, so if I look at something and think "oof, how would I ever write a test for that?" I'll usually delete it and start over. When I have something that should be working, I test it in a more functional/integrative manner, and move on. Later, I'll write unit tests when I need to. If I want to refactor something, or drastically change the implementation of something, I'll write out some tests beforehand to be sure that the pre and post behaviors match. I've always thought that TDD is just premature optimization. You're optimizing for the idea that you -- or someone -- will later need to make large enough changes to your code that you'd worry about breaking it. In my experience that's fairly rare, and you spend less time overall if you just write the tests as you need them, not up-front. Yes, writing a test when the code is fresh in your mind will be faster than writing it much later, but then you're writing a ton of test code that likely won't be necessary. An objection I hear to this is that you're not just writing tests for yourself, you're writing tests for the others who will need to help maintain your code, perhaps after you're gone. I'm somewhat sympathetic to this, but I would also say that if someone else needs to modify my code, they damn well better first understand it well enough such that they could write tests before changing it (if they deem it necessary). Anything else is just irresponsible. (Note that I primarily work in strongly statically typed languages. If I were writing anything of complexity in ruby/python/JS/etc., I don't think I'd feel comfortable without testing a lot of things I'd consider trivial in other languages.) (Also note that some things are just different: if you're writing a crypto library, then you absolutely need to write tests to verify behaviors, in part because you're building something that must conform to a formal spec, or else it's less than worthless.)
- defined 9y ago
- algesten 9y agoMy main issue with unit testing is what defines a unit? Throughout my career I find tests that tests the very lowest implementation detail, like private helper methods, and even though a project can achieve 100% coverage it still is no help avoiding bugs or regression. Given a micro service architecture I now advocate treating each service as a black box and focus on writing tests for the boundaries of that box. That way tests actually assist with refactoring rather than be something that just exactly follows the code and breaks whenever a minor internal detail changes. However occasionally I do find it helpful map out all input/output for an internal function to cover all edge cases. But that's an exception.
- ardit33 9y agoI agree with you. That's called functional testing, and it is very useful, but it is not unit testing. Unit testing: Test all methods and paths of a class, even private ones. Functional Testing: Test the public api of a class/service only. If something is wrong internally, it will be caught without having to write countless of little tests. ROI of functional testing is high, as it is usually done with real data. In my opinion unit testing is a huge waste of time. Most of the tests devolve int mock objects, calling mock methods, and doing this that really don't help to find real world bugs, where two unit tests pass, but their methods produce the wrong output.
- camus2 9y ago> even private ones. You don't test private API, that's why they are declared private, it makes absolutely no sense to test private members. Now the use of the private member might lead to a different code path : it has to be tested. Unit Testing : test one unit ( a class for instance ) in isolation, which means all collaborators (the classes the tested class depends on) have to be stubbed or mocked. Functional Testing : test multiple unit at the same time to check the behavior of an entire functionality, that's why it is called functional testing. Stub the IO. End-to-end testing: Test the entire system with the IO.
- bpicolo 9y ago
- devrandomguy 9y agoIf you can prove that your testing process is perfect, then your entire development process can then be reduced to the following, after the test suite is written: cat /dev/random | ./build-inline.sh | ./test-inline.sh | tee ./src/blob.c && git commit -Am "I have no idea how this works, but I am certain that it works perfectly, see you all on Monday!" && git push production master --force When presented like this, relying on human intelligence and experience doesn't seem like such a bad thing after all. Just so we're clear, my username was not inspired by this scheme.
- anamexis 9y agoIt seems to me that such a "perfect" testing process would basically amount to declarative programming.
- ZeroGravitas 9y agoSeveral other people are complaining about 100% coverage sometimes being misleading. One way to test your test is to randomly modify your code e.g. swapping a greater than for a less than. If your tests still pass, then they obviously missed this change in behaviour. This is known as mutation testing. https://en.wikipedia.org/wiki/Mutation_testing https://en.wikipedia.org/wiki/Mutation_testing I've tried it a few times, I generally found it too slow to be an everyday part of my routine, but interesting tool to have in your belt for e.g. evaluating test in a codebase you didn't write yourself.
- rjdavis3 9y agoHave you ever tried it with one of the automated tools? I use Pitest extensively to mutate my Java code bases and found it is not too much slower then regular line and branch code coverage tools when you use a history file so only diffs are needed. The trade off of a few more seconds of build time is worth the benefit of the coverage report and being able to fail the build if coverage drops too low for me. http://pitest.org/quickstart/maven/ http://pitest.org/quickstart/maven/
- lacampbell 9y agoI feel like this high test coverage thing can only work if you have tight modules, tight interfaces, and you only bother testing at module boundaries. So the test cases almost function as a bit of executable API documentation - here's the method name, here's what it does, here's the contracts and/or static types, and.... given this input, you should get this output. Do it for the high level bits you actually expose. If you're exposing everything, tests won't really save you - architecture and modularity are more fundamental and should be tackled first. If you're writing a big ball of mud, what benefit do you get testing a mudball?
- jowiar 9y agoOne of the pressures for 100% coverage is working in a non-typesafe language. The gospel of coverage largely evolved in the Ruby community, where I often see test suites that look like a handrolled typechecker.
- state_less 9y agoUnit tests are a poor substitute for correctness. Many unit tests does not a strong argument make. Unit tests are typically inductive. Developer shows case A, B and C give the expected results for function f. God help us if our expectations are wrong. So, you're saying since A, B and C are correct therefore function f is correct. Well that may be, or maybe A, B and C are trivial cases, in other words, you've made a weak argument. 100% test coverage sounds like lazy management. Alas, the manager may have worked their way via social programming rather than computer programming. In such cases, better to say you have 110% test coverage.
- waibelp 9y agoThis remembers me of recent projects where developers started to mock every piece of code.. The result was that all tests passed while the codebase exploded in real environments. In my opinion the best advice is to force developers to use their brains. I know, there are a lot of sh*tty CTO/CEO/HoIT/SomeOther"Important"Position people out there seeing them as code monkeys and saying that developers are not paid to think but in that case the best thing developers could do is learn to say "NO"... My experience with that kind of people is that they need to learn the meaning of "NO" instead of wasting time and money in the end of the day.
- hasenj 9y agoI think unit testing makes sense when you have a function doing some math that can't be easily verified to be sensible by merely glancing at the code for two minutes. I'm not sure there's much use for it in other scenarios.
- lemonsqueeze 9y ago95% coverage is pretty good balance I find. There's something magical about being pretty sure things still work after any kind of refactor or new requirement. I have been in situations like the author presents... decided not to test it and lo and behold it breaks production and causes $10,000 worth of revenue loss.
- eksemplar 9y agoWe've almost stopped unit testing. We still test functionality automatically before releasing anything into production, but we're not doing a unit test in most cases Our productivity is way up and our failure rates haven't changed. It's increased our time spent debugging, but not by as much as we had estimated that it would. I won't pretend that's a good decision for everyone. But I do think people take test-driven-development a little too religiously and often forget to ask themselves why they are writing a certain unit test. I mean, before I was a manager I was a developer and I also went to a university where a professor once told me I had to unit test everything. But then, another professor told me to always use the singleton pattern. These days I view both statements as equally false.
- vacri 9y agoAt one place I work, the target is 80% rather than 100%. Seems to be a saner target than full coverage.
- dcw303 9y agoI'm curious - how old are the systems you're working on? In my experience, unit tests don't catch many bugs when the code is fresh. But when it's five years old with many modifications over the code base, some dumb little test that you thought was a waste of time is now alerting you to what would have been a horror regression. In other words: Even though it feels like it's slowing you down now, if you write tests while the functionality is still fresh in your mind, and you capture your assertions well, they'll pay off dividends in the future once you've forgotten what some block of code does.
- raarts 9y agoYeah good luck explaining those 5 years to the business manager.
- eksemplar 9y agoI work in the public sector in Scandinavia and some of our oldest systems still in service run on an old tandem. So some of it is pretty old. This gives us some unique abilities in terms of modeling our productivity of course, because we started measuring before anyone thought up unit testing. Over the past 15 years, unit testing has failed to produce anything positive, and test driven development has been an absolute disaster. That being said, this isn't something which will be universally true. A lot of the software we use isn't build by us, and I'm certainly that a lot of the suppliers on that software use unit tests quite extensively. For the things we build ourselves, however, there has been almost no value in adopting modern test philosophies. You say that systems have to be able to be worked on 5 years from now, but the truth is that most of our systems transport data and rarely live 5 years without getting rewritten to deliver better performance, higher levels of security or simply because the business has changed completely. A lot of it hold very few responsibilities as well, making it extremely obvious what to fix when a service breaks. Don't get me wrong, we've seen problems we wouldn't have with 100% coverage. But that doesn't matter when spending resources fixing them is still a net positive on every account.
- elchief 9y agoWhat you actually want to do is test the methods with the highest cyclomatic complexity first (where it's greater than 1) IntelliJ has a plugin
- ZeroGravitas 9y agoMost standard unit coverage tools will do this for you, often with the CRAP (Change-risk anti-patterns) score: http://www.ncover.com/blog/change-risk-anti-patterns-code-complexity/ http://www.ncover.com/blog/change-risk-anti-patterns-code-co... "This is based on the methodology that the more complex code is the more likely it is to have errors, hence the need for greater code coverage. To better understand, let’s assume a scenario where you wanted to keep your change risk anti-patterns score below 30. To maintain this level with a code set that has a cyclomatic complexity of 10, you would need to achieve a code coverage (as defined by branch coverage) ratio of approximately 42%. If however, the complexity of your code was greater and you had a cyclomatic complexity of 20, you would need almost 72% code coverage if you wanted to maintain the same risk level. Complexity increases risk. Testing decreases risk. The change risk anti-patterns score give you a metric to measure the correlation between the two."
- Ace17 9y agoSee the paper "On the Danger of Coverage Directed Test Case Generation". http://link.springer.com/chapter/10.1007%2F978-3-642-28872-2_28 http://link.springer.com/chapter/10.1007%2F978-3-642-28872-2... The idea is that a test suite can have 100% coverage and still be a very bad test suite.
- Ace17 9y agoHaving 100% code coverage is like having 0 warnings (although it certainly is a lot harder). In this situation, your tools are not telling you "all's good", but rather "I can't detect anything suspect here". There's a good chance that the dev time needed to go from 90% coverage to 100% coverage might be better spent somewhere else.
- keithnz 9y agoI've heard Kent Beck talk about having the smallest amount of tests that give you confidence. Which he also gave as an answer here: http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests/153565#153565 http://stackoverflow.com/questions/153234/how-deep-are-your-... But I know a lot of people in the early days of XP went to extremes, 100% code coverage, mutation tools for every condition to ensure unit tests broke in expected ways, etc. But they were more experiments in pushing the limits rather than things that gave productivity gains.
- shusson 9y agoHas anyone read any papers about the relationship between code coverage and defects?
- collyw 9y agoNot exactly what you asked for but this compares defects and different types of testing. Unit testing is one of the less effective ways of finding bugs. https://kev.inburke.com/kevin/the-best-ways-to-find-bugs-in-your-code/ https://kev.inburke.com/kevin/the-best-ways-to-find-bugs-in-...
- johnwatson11218 9y agoNot sure if this is already mentioned but for me the most concise illustration of this fallacy was in The Pragmatic Programmer book. They had a function like this: double f( double x ) { return 1/ x; } They pointed out that it is trivial to get 100% coverage in test cases but unless your tests include passing in 0 as the parameter you are going to miss an error case.
- dullgiulio 9y agoExactly, or to put it another way: you are not forced to cover the code you didn't write; in this case, the non-zero check before the division. Mix this with unchecked exceptions, mocking of any real-world interaction that will generate errors in productions, and testing becomes a cargo-cult of quality.
- voidpointer 9y agopassing 0 to f returns infinity (IEEE 754) Not arguing the point here. It's just a terrible example.
- LgWoodenBadger 9y agoI thought you'd get NaN, not Inf. Of course I'm probably misremembering.
- johnwatson11218 9y agoI think what the above returns depends on the language. In java I think there is an actual run time exception for this. The point of the original comment is to show how 100% coverage is easy to achieve but often meaningless. There is some other more abstract concept related to the classes of input that can possibly be passed to a method. IMHO 100% coverage and "test first" have done more harm than good to the cause of automated testing.
- bluGill 9y agoYou are both correct and wrong: I want X/0 to immediately crash (fatal error) before the bad data that caused my to try to divide by zero gets propagated farther. Now that I think about it, I really want X/Y where Y is "close" to 0 to crash too. Of course I made the above up on the spot, but it is a reasonable thing to do for some situations. Those who know floating point math are well aware that dividing by something close to zero tends to result in very large errors, vs the true answer. (particularly if the division is part of a larger calculation)
- rumcajz 9y agoI've seen a project with 100% unit test coverage, yet no e2e tests. Nobody knew whether the product worked at all.
- jondubois 9y agoAgreed. I would rather have 5% test coverage that checks against all risky edge cases/inputs than 100% test coverage that checks against arbitrary, low-risk inputs. Writing tests to confirm the simplest, most predictable use cases is a waste of time - Those cases can be figured out very quickly without automated testing because they are trivial to reproduce manually.
- EngineerBetter 9y agoI'd suggest the tragedy here is an absence of kaizen and team processes that foster continuous improvement. If folks are doing inefficient things, that should be caught by the team in a retro or similar.
- reledi 9y agoI've found that some bootcamps are responsible for this attitude as they preach to have 100% coverage. And no one really questions the experienced and heavily opinionated teacher. It's good to use hyperbole black and white when teaching so the point comes across easier. But they should be made aware of caveats before they graduate at least.
- crimsonalucard 9y agoIt's a case of convention over common sense.
- koonsolo 9y agoI use the following list to decide on creating a unit test or not. More yeses means a unit test is a good idea. 1. Is it hard to instantly test the code when implementing it? (Might be the case for library code) 2. Is there a chance the underlying implementation might change (and so might break in the future)? 3. Will the interface of the class remain stable? (If not unit test needs to be rewritten too) 4. Will functional tests pass when something breaks in this class?
- alkonaut 9y agoI often try to write a unit test just to see whether it's possible. If not (i.e. one of the questions was no) there is always a chance that the design can be improved. E.g. can the interface be minimized to something that is stable, and simpler to test? Testable code tends to be better (have less dependencies, less state etc) than non testable code. So making it testable is a goal in itself, even if the tests are never even committed.
- WalterBright 9y agoOf course any metric can be rendered useless if one "works the metric" rather than the intent of the metric. But in my experience, code covering unit tests have correlated strongly with faster development and far fewer bugs being uncovered in the field.
- dragonwriter 9y ago> Of course any metric can be rendered useless if one "works the metric" rather than the intent of the metric. If the metric actually measures the figure of merit rather than a proxy with no necessary linkage, this is not true; working the metric then is working the intent of the metric. The extent to which a metric can be "worked" distinct from its intent is exactly the extent to which it is measuring something disconnected from what it is used to assess.
- zbx223 9y agoI do not understand how this is connected to what Walter Bright wrote. He has an excellent point.
- ZeroGravitas 9y agoEven if the measure is accurate, there's an opportunity cost for spending time on things that are also important, but not measured. You'd need a measure that covered everything, from sufficient speed optimisations to fulfilling customer desires to complying with all relevant legal requirements, which basically can't exist.
- reledi 9y agoStriving for 100% coverage is an expensive mistake because as a testing indicator it gives you a false sense of security. But someone has to pay for the time spent writing and maintaining those tests, and fixing the bugs that are still there. I much prefer to use code coverage as a weak indicator for finding dead code.
- chmike 9y agoWhile 100% code coverage doesn't guarantee 0% bug, it's useful to easily detect new untested code addition and possible bug addition. Another point is that the code looks obviously right by visual inspection, but we want to automate the check. Relaxing the 100% coverage is a lazy slippery slope I don't take with my code. The danger of 100% percent coverage is that the goal of tests becomes the 100% code coverage and not bug detection anymore.
- iamleppert 9y agoThe worse the developer, the more tests he'll write. Instead of writing clean code that makes sense and is easy to reason about, he will write long-winded, poorly abstracted, weird code that is prone to breaking without an extensive "test suite" to hold the madness together and god forbid raise an alert when some unexpected file over here breaks a function over there. Tests will be poorly written, pointless, and give an overall false sense of security to the next sap who breaths a sigh of relief when "nothing is broken". Of course, that house of cards will come down the first time something is in fact broken. I've worked in plenty of those environments, where there was a test suite, but it couldn't be trusted. In fact, more often than not that is the case. The developers are a constant slave to it, patching it up; keeping it all lubed up. It's like the salt and pepper on a shit cake. Testing what you do and developing ways to ensure its reliable, fault-tolerant and maintainable should be part of your ethos as a software developer. But being pedantic about unit tests, chasing after pointless numbers and being obsessed with a certain kind of code is the hallmark of a fool.
- scalatohaskell 9y agoThis hits home with me. Incompetent developers need dogmatisms (100% unit coverage!!!! etc)
- adamlett 9y agoNo, inexperienced developers need dogmatisms. Subtle difference perhaps, but it's worth remembering that every competent developer started out being inexperienced, and that it's nothing to be ashamed of.
- scalatohaskell 9y agoYou are correct. I was once inexperienced and was very dogmatic.
- lmm 9y agoNo. We fetishize experience too much. I've known a great many competent inexperienced developers who could weigh tradeoffs and use the right approach to a given task, and a far too many highly experienced developers who destroyed productivity through dogmatism.
- tommikaikkonen 9y agoProperty-based testing has made testing more productive and fun for me. You write a few lines of code that produce a large amount of tests. The idea is obviously so useful, I'm surprised it's uncommon in practice. When you think about coverage in terms of inputs applied instead of statements executed, property-based testing is far more productive than writing tests by hand. It's not a silver bullet though. Some property-based tests are easy to write but offer little value. Sometimes you spend more time writing code to generate the correct inputs than the value of the test warrants. It has a learning curve. Still, I think it is the most powerful tool you can master for testing.
- afpx 9y agoMany of us have made similar mistakes (especially early in our careers) when taking on new techniques for which we became particularly enthralled. That's why it's a good idea to have a couple 'elders' on-staff so as to not allow youthful passion to wreck havoc. They tend to keep teams pragmatic and lazy (a good thing, in programming). For instance, I remember all the bad code that I wrote and read circa 1997-1999, after design patterns became the rage.
- hultner 9y agoBack when I learnt Haskell we had a lecturer named John Hughes who had co-authored a tool named QuickCheck[1]. We used this tool extensively throughout the course, with it testing were quite simple and writing elegant generators were a breeze. In my experience, these test did a much more greater job at finding edge cases then many unit tests I've seen in larger close to full coverage TDD projects. As with much else TDD should be a tool with the ultimate goal of aiding us in writing correct and less bug riddled code, once the tool adds more work it's no longer offering much aid. [1] . https://en.wikipedia.org/wiki/QuickCheck https://en.wikipedia.org/wiki/QuickCheck
- ocharles 9y agoAlso see https://github.com/hedgehogqa https://github.com/hedgehogqa https://hackage.haskell.org/package/smallcheck https://hackage.haskell.org/package/smallcheck
- pg314 9y agoThe article illustrates what happens when you have inexperienced or poor developers following a management guideline. To see how 100% coverage testing can lead to great results, have a look at the SQLite project [1]. In my experience, getting to 100% takes a bit of effort. But once you get there it has the advantage that you have a big incentive to keep it there. There is no way to rationalise that a new function doesn't need testing, because that would mess up the coverage. Going from 85% to 84% coverage is much easier to rationalise. And of course 100% coverage doesn't mean that there are no bugs, but x% coverage means that 100-x% of the code is not even run by the tests. Do you really want your users to be the first ones to execute the code? As an anecdote, in one project where I set the goal of 100% coverage, there was a bug in literally the last uncovered statement before getting to 100%. [1] https://www.sqlite.org/testing.html https://www.sqlite.org/testing.html
- thehardsphere 9y agoDoesn't what you're writing actually influence whether 100% coverage is a worthy goal or not? I mean, SQLite is a good example of something where 100% coverage would actually be useful, because it tries to maintain compatibility with the SQL spec and with Postgres (largely because Postgres complies with the spec). Testing that 100% makes a lot of sense. Suppose you're instead doing something very UX/UI driven. Why bother trying to cover that 100% with automated tests, when change is going to be driven by the whim and fancy of anyone who sits down in front of it?
- pg314 9y ago> Doesn't what you're writing actually influence whether 100% coverage is a worthy goal or not? Yes, absolutely. > Suppose you're instead doing something very UX/UI driven. Why bother trying to cover that 100% with automated tests, when change is going to be driven by the whim and fancy of anyone who sits down in front of it? It might make perfect sense from a business standpoint to have no tests at all.
- BJanecke 9y agoSo generally, I write a test when I want to make an assumption an certainty. If I can't be certain that something is doing what it's supposed to I write a test for it, make sense? So ``` Int => add(x, y) => x + y; ``` Doesn't get a test, however ``` Int => formulateIt(x, y) => (x * y)^y ``` Does
- saltedmd5 9y agoThe big error being made in this article (and most of the comments here) is the assumption that the purpose of unit tests is to "catch bugs." It isn't. The purpose of unit tests is to document the intended behaviour of a unit/component (which is not necessarily a single function/method in isolation) in such a way that if someone comes along and makes a change that alters specified behaviour, they are aware that they have done so and prevented from shipping that change unless they consciously alter that specification. And, if you are doing TDD, as a code structure/design aid. But that is tangential to the article.
- dahauns 9y agoThe big error being made in this article (and most of the comments here) is the assumption that the purpose of unit tests is to "catch bugs." More like "in countless articles, comments, talks and projects over, say, the last decade".
- saltedmd5 9y agoYes - it's pretty frustrating to try and have an important discussion when 99% of the time it is framed incorrectly from the start.
- xg15 9y agoI agree (mostly) with the authors standpoints, but his arguments to get there are not convincing: > You don't need to test that. [...] The code is obvious. There are no conditionals, no loops, no transformations, nothing. The code is just a little bit of plain old glue code. The code invokes a user-passed callback to register another callback and specifies some internal logic if that callback is invoked. I personally don't find that obvious at all. Others may find it obvious. That's why I think, if you start with the notion "this is necessary to test, that isn't", you need to define some objective criteria when things should be tested. Relying on your own gut feeling (or expecting that everyone else magically has the same gut feeling) is not a good strategy. If I rewrite some java code from vanilla loops-with-conditionals into a stream/filter/map/collect chain, that might make it more obvious, but it wouldn't suddenly remove the need to test it, would it? >"But without a test, anybody can come, make a change and break the code!" >"Look, if that imaginary evil/clueless developer comes and breaks that simple code, what do you think he will do if a related unit test breaks? He will just delete it." You could make that argument against any kind of automated test. So should we get rid of all kinds of testing? Besides, the argument doesn't even make sense. No one is using tests as a security feature against "evil" developers (I hope). (One of) the points of tests is to be a safeguard for anyone (including yourself) who might change the code in the future and might not be aware of all the implications of that change. In that scenario, it's very likely you change the code but will have a good look at the failed test before deciding what to do.
- antirez 9y agoCode coverage is an illusion, since what you want is actually "possible states coverage". You can cover all the lines of your code and still cover a minority of the possible states of the program, and especially a minority of the most probable states of the program when the actual users execute it, or you can cover 50% of your lines of code and yet cover much more real world states, and states for which it is more likely to find a bug. I think that more than stressing single features with unit tests, it is more useful to write higher level stress tests (fuzz tests, basically) that have the effect of testing lines of code as a side effect of exploring many states of the program. Specific unit tests are still useful, but mostly in order to ensure that edge cases and main normal behavior corresponds to the specification. As in everything it is develooper's sensibility that should drive what test to write.
- divan 9y agoGood example of Goodhart's law: "When a measure becomes a target, it ceases to be a good measure." https://en.wikipedia.org/wiki/Goodhart%27s_law https://en.wikipedia.org/wiki/Goodhart%27s_law
- biztos 9y agoA lot of people here seem to have strong opinions against 100% coverage, so I'll risk their ire with my strong opinion in favor. If you have, say, 95% coverage -- and most corporate dev orgs would be thrilled with that number -- and then you commit some new code (with tests) and are still at 95%, you don't know anything about your new code's coverage until you dig into the coverage report. Because your changes could have had 100% coverage of your new thing but masked a path that was previously tested; or had 10% but exercised some of the previously missing 5%. If you have 100% coverage and you stay at 100% then you know the coverage of your new code: it's 100%. Among other things this lets you use a fall in coverage as a trigger: to block a merge, to go read a coverage report, whatever you think it warrants. Also, as has been noted elsewhere, anything other than a 100% goal means somebody decides what's "worth" testing... and then you have either unpredictable behavior (what's obvious to whom?) or a set of policies about it, which can quickly become more onerous than a goal of 100%. It's important to remember that the 100% goal isn't going to save you from bad tests or bad code. It's possible to cheat on the testing as well, and tests need code review too. There's no magic bullet, you still need people who care about their work. I realize this might not work everywhere, but what I shoot for is 100% coverage using only the public API, with heavy use of mock classes and objects for anything not directly under test and/or not stable in real life. If we can't exercise the code through the public API's then it usually turns out we either didn't rig up the tests right, or the code itself is poorly designed. Fixing either or both is always a good thing. I don't always hit the 100% goal, especially with legacy code. But it remains the goal, and I haven't seen any convincing arguments against it yet. Open the flame bay doors, Hal... :-)
- Hermel 9y agoThe problem with the "100%" goal is that it provides a false sense of safety. There can still be plenty of bugs in code that has "100%" test coverage. The 100% are an illusion. To truly get to 100%, it does not suffice that every line of code is executed, you must also test all valid inputs - which is utopic.
- biztos 9y agoI concede that for people who don't understand what unit testing does, 100% might provide a false sense of security. But I don't think that's a problem with the goal... that would be like saying the problem with staying fit is that it provides a false sense of longevity, whereas you could still get brain cancer or be hit by a car. If you know what's going on -- and hopefully you do if you're making decisions like whether to pursue a 100% coverage goal -- then your point is (I hope) self-evident. It's also why code review is necessary and not just the coverage metric. 100% coverage is not "100% of all things that could happen," it's "100% of all code execution paths" and (hopefully) a sample of all known types of valid and invalid inputs. Anyway, so far I've found that most developers respond pretty well to the 100% idea if you explain its utility and what it does and doesn't get you. From a ridiculously small sample, granted. The utopian risk, I think, is much more around the hope that people high up the food chain in your organization actually understand this stuff. Consider how many VPs of Engineering did a year or two of engineering work (before unit testing was in vogue) then got that MBA and have been busy taking meetings ever since.
- yoav_hollander 9y agoOne point already made by several people on this thread is that code coverage, while helpful, is not enough (and perhaps is not even the best bang for the buck). In hardware verification (where I come from, and where the cost of bugs is usually higher), "functional coverage" is considered more important. This is usually achieved via constraint-based randomization (somewhat similar in spirit to QuickCheck, already mentioned in this thread). I tried to cover (ahem) this whole how-to-use-and-improve-coverage topic in the following post: https://blog.foretellix.com/2016/12/23/verification-coverage-and-maximization-the-big-picture/ https://blog.foretellix.com/2016/12/23/verification-coverage...
- vinceguidry 9y agoI noticed the author was speechless in two situations, both of which involved "but we write all our tests in <test-framework>." This is legitimate and should be taken more seriously by the author. Codebases serve businesses and businesses value legibility over efficacy. It's more important to them to have control over their assets than to have better assets. Using one test framework is in perfect service of that goal. It's inefficient in that it will take future developers more time to understand that code. But fewer architectural elements means that you can get by with less senior programmers. Imagine if you went onto a software project and they were using 6 different databases because every time they had a new kind of data that they wanted to access differently, they reached for another database rather than use the one they had. Of course nobody would ever do that, well I hope anyway, but I do see a lot of unnecessary architectural complication in projects in service of "using the right tool for the job." And it can balloon. A new test framework has to work in your CI framework. You need to decide how to handle data. It's not a huge decision, but it's more complicated then most devs would think and it'll take up more of your time than you'll expect. You can generalize this to the the main thrust of the article. 100% code coverage is not a bad goal to want to hit. Sure, you're going to get a lot of waste. But you're not paying for it, your employer is. And your employer might have a different idea of which side of the tradeoff he wants to be on and where to draw the line. You know the code way better than they will, but they know the economics far better than you ever could.
- userbinator 9y agoBut remember nothing is free, nothing is a silver bullet. Stop and think. I'm going to be the one to point at the elephant in the room and say: Java. More precisely, Java's culture. If you ask developers who have been assimilated into a culture of slavish bureaucratic-red-tape adherence to "best practices" and extreme problem-decomposition to step back and ask themselves whether what they're doing makes sense, what else would you expect? These people have been taught --- or perhaps indoctrinated --- that such mindless rule-following is the norm, and to think only about the immediate tiny piece of the whole problem. To ask any more of them is like asking an ostrich to fly. The method names in the second example are rather WTF-inducing too, but to someone who has only ever been exposed to code like that, it would probably seem normal. (I counted one of them at ~100 characters. It reminds me of http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom... ) Many years ago I briefly worked with enterprise Java, and found this sort of stifling, anti-intellectual atmosphere entirely unbearable.
- vitro 9y agoMethod names got my eyebrows up as well and I spend a lot of time in Java. I should consider myself lucky.
- misja111 9y agoIt might even make sense for many of those enterprise developers to have a set of best practices that they can blindly follow. Not every developer has that many years of experience or is sharp enough to be able to identify the cases where unit testing makes sense, and those where it doesn't.
- vitro 9y agoTo paraphrase: "Premature testing is the root of all evil". How I do it is going from rough testing of pages and components to granular testing of those parts which had some error. For pages, I just run them to see if they display without producing errors, same goes for critical components. This gets me the feeling of roughly tested and from the user perspective working system with little time investment. Then I test critical business logic, but usually only after some error was reported. Mind though that I am freelance developer unconstrained by organizational rules.
- rothron 9y agoI don't think I know anyone that do TDD. Uncle Bob has indoctrinated a few zealots into that mindset, but it all comes off as crazy to me. A germ of a good idea taken way too far. People of that school tend to write tests that test implementation rather than functionality. As a result you get fragile tests that break not telling you what went wrong but how the implementation has changed. Good tests should test behavior. A change in implementation shouldn't break the test.
- BeetleB 9y ago>Uncle Bob has indoctrinated a few zealots into that mindset Even though Uncle Bob's advice agrees with this submission: https://news.ycombinator.com/item?id=14301466 https://news.ycombinator.com/item?id=14301466 >People of that school tend to write tests that test implementation rather than functionality. As a result you get fragile tests that break not telling you what went wrong but how the implementation has changed. Also another thing Uncle Bob does not advocate. From his stance, if most of your failing tests are due to refactors (changing implementation), and not bugs, you need to redesign your tests. Abstract out your interfaces so tests are not too dependent on the actual implementation. Tests are first class code, not to be treated differently from your main code base - they should abide by code standards, and they need to be architected as much as your regular code. (Easier said than done). The thing is, I'm not even an Uncle Bob fan. But it's crazy how neither his fans nor his detractors see the nuances in what he says - be it his videos or his blog posts. What is it about programmer types that makes them see everything in binary?
- circlefavshape 9y ago/me raises hand on the pro-testing side I've been programming for a living since 1996, and only recently started to do TDD in the normal sense of writing unit tests before writing code. I've found to it to be an enormous help with keeping my code simple - the tests or the mocking getting difficult is a great indicator that my code can be simplified or generalised somehow I argued for functional instead of unit testing for years, but it was only when a team-mate convinced me to try unit testing (and writing the tests FIRST) that the scales fell from my eyes. Unit testing isn't really testing, it's a tool for writing better code. BTW from an operational perspective I've found it's most effective to insist on 100% coverage, but to use annotations to tell the code coverage tool to ignore stuff the team has actively decided not to test - much easier to pick up the uncovered stuff in code review and come to an agreement on whether it's ok to ignore
- saltedmd5 9y ago+1 - speaking as an ex-sceptic, the only people I know that don't like unit testing and/or TDD are the people who either misunderstand their purpose or have not learned how to utilise them properly yet. Once TDD "clicked," the quality of my code shot up very rapidly indeed.
- misja111 9y agoI agree that TDD and unit tests can help you when writing code. But once your code has been written, are all those unit tests still useful? Or do they get in your way when you do large scale refactoring? I have seen quite a few codebases with loads of unit tests that might once have been useful, but now were just slowing down development. Many of them should have been thrown away and replaced by a couple of functional tests, however the sunk cost fallacy always kicked in, nobody wanted to throw away all the hours of work that had gone into making them and not to forget the precious code coverage as well.
- circlefavshape 9y ago> nobody wanted to throw away all the hours of work that had gone into making them This is a REALLY common developer attribute that drives me nuts - people don't want to throw away stuff that they put a lot of effort into.
- deleted 9y ago[deleted]
- EugeneOZ 9y agoLaziness is a kind of populism. Such articles will be always upvoted.
- TallGuyShort 9y agoHe's not preaching laziness, he's against hitting metrics solely for the sake of hitting the metric when the metric is irrelevant. 100% code coverage? Good. 100% code coverage in a single test framework at 10 times the development cost when it's still not really 100%? Stupid. Working smarter isn't always in opposition to working harder.
- EugeneOZ 9y agoNo, he is exactly against covering simple code by tests. It's explained below first sentence. It is funny how things turn around. For fifteen years I have been preaching TDD (Test-driven development, or as it used to be called: test-first approach), or at least for developers to write some unit tests. However, in recent times I have found myself saying more often, "Why did you write that test?" instead of, "You should write a test." Answer to that "funny turn around" sentence is simple - he is getting older and more lazy. Each of us will, each of us should fight with it. And after that, example of what I'm talking about: It seems that he had trouble using Mockito to test the following piece of code: ... I think he was very surprised with my response: "You don't need to test that."
- JoeAltmaier 9y agoA concise word for 'older and lazy' might be 'experienced'
- EugeneOZ 9y agoNot always, not in this case definitely.
- JoeAltmaier 9y ago
- mirko22 9y agoThe title does not mean anything and is basically a click bait in my opinion as it sounds cool to trash some ideal. That said, the magic 100% number is far removed from reality and does not represent anything by itself. 100% coverage on project of which size? Imagine you have a single script project that does exactly one thing and 2 test are enough to verify that it works without doing it manually? That is not the same as writing test for file system or tests which consists mostly of mocks upon mocks upon mocks. I think the real problem is someone comes up with an idea, like TDD, tells people about it, some people hear about, start preaching it, some people start believing it and nobody actually think things through, usually cos they don't have experience (it's not a fetish as someone said). Like everything in life, you have to think things through before doing them, ask your self is this worth doing and when it is worth doing. You can't just say: "Oh we are doing TDD thus everything must be done in TDD way". For people that say tests are useless, or good code does not need tests, I ask, when you make a change do you still make sure your code works by hand? And if you do make sure, why don't you automate that? You are a programmer after all. And for those that say you need to test everything, well you don't, specially if you need mock most of it or it is really not that important piece of code as it is dev tool or something. What you want to make sure works is customer/user facing stuff that must work for you to get paid and you want to be able to verify this at any time of day without losing hours clicking around checking for stuff. So this is not straight forward, 100% means nothing without context and doing anything in excess and without valid reasons is pointless or even harmful. And this has nothing to do with programming but life in general.
- michaelfeathers 9y agoCoverage isn't the goal. The goal is understanding. Write a test if you don't feel confident that a piece of code does what you think it does. If you're not sure what it does now, there's little chance that you or anyone else will in the future, so write a test to understand it and to make that understanding explicit. Use curiosity as a driver.
- sAbakumoff 9y agoAlso, the code of a test could be used as the documentation. Just recently I started using bleve(https://github.com/blevesearch/bleve https://github.com/blevesearch/bleve). Documentation is quite poor, but I was able to learn the product entirely from the code of its unit tests. That's not normal though.
- xutopia 9y agoThe tragedy he mentions aren't 100% code coverage. It's more about people using the wrong tools for the job and using 100% coverage as an indication that everything is fine. 100% coverage for my team means that we were intentional about our code. It's not hard at all to have 100% coverage in a Ruby application as it is possible to do a lot with very little code. Furthermore it allows us to bring in a junior on the team because we know they have a safety net. Also for the record we do code reviews and are very thoughtful about the code we write. 100% coverage does not stop the possibility of some bugs inserting themselves somewhere.
- snarf21 9y agoExactly this. One problem with TDD, etc. is that you are limited by the quality of your tests. 100% means nothing if you don't have a try/catch around a cache lookup that could should never fail but could and not get handled.
- bluGill 9y agoNo, that isn't a limitation with TDD. That is a limitation of imagination, and can happen to any programmer. (proving the code correct is most likely to catch this - but even that can fail in some cases) TDD just means that when discover the bug and add the missing try/catch you don't accidentally break something else.
- Dove 9y agoAutomated tests are code, and come with all the engineering and maintenance concerns of 'real' code. They don't do anything for your customers, though, so are only appropriate when they actually make your work faster or safer. Automated tests are a spec, and are exactly as hard to write completely and correctly, and as easy to get wrong in ignorance, as a 'real' spec. If you find them easy to write, odds are good you would find the code easy to visually verify as well - which is to say, you're working on a trivial problem. They have their place, but that place is not everywhere. It is where they are efficient and valuable. I particularly look for places where they are like the P half of an NP problem, an independent estimate of the answer to a math problem. If you ever find yourself writing the same code twice, unless it's a safety-critical system or something, that's a moment to stop and reflect on the value of what you are doing.
- jganetsk 9y agoHe's right, but he's conflating 100% code coverage with using mocks with writing tests. Always write tests. And strive for maxmium coverage. But make sure you write the right kinds of tests: - Don't overuse mocks. Mocks don't represent real conditions you would actually see in production. Favor using real dependencies over mocks. - Don't overspecify your tests. Test only publicly specified parts of the contract. Things that you need to be true and that the callers of the module expect to be true. And yes, you will change the test when the contract changes.
- scottschulthess 9y agoOnly mock things that would be difficult to setup in a test, is my approach. Then also set up some other ci jobs for the difficult stuff when we have time.
- falcolas 9y agoIMO, if I ever have 100% code coverage, I did something wrong. The best I can usually achieve is 95-98%, because of my defensive coding to warn about the "impossible" use cases. Escape a `while True` loop? Log it, along with the current state of the program, and blow up (so we can be restarted). Memory allocation error? Log it. The big "unexpected exception" clause around my main function? Log it. If I do hit those in testing, my code is wrong.
- xmatos 9y agoBuild tests against your app`s public interface. On a web app, that would be your controllers or API. That will give you good coverage, while avoiding too simple to be useful unit tests. It's really hard to foresee all possible input variations and business logic validations, but that doesn't mean your test suite is useless. It just means it will grow everytime you find a new bug and you are guaranteed that one won't happen again...
- bluGill 9y agoOn a small app that is a good idea. However on a massive app you need to break things down more. I work on a project with > 10 million lines of code, there is no way any human can understand everything in detail. As a result I need to test at a smaller scale which is not my public interfaces, but my interfaces to other people.
- mannykannot 9y agoThere are a few relevant facts that should be known to everyone (including managers) involved in software development, but which probably are not: 1) 100% path coverage is not even close to exhaustively checking the full set of states and state transitions of any usefully large program. 2) If, furthermore, you have concurrency, the possible interleavings of thread execution blow up the already-huge number of cases from 1) to the point where the latter look tiny in comparison. 3) From 1) and 2), it is completely infeasible to exhaustively test a system of any significant size. The corollary of 3) is that you cannot avoid being selective about what you test for, so the question becomes, do you want that decision to be an informed one, or will you allow it to be decided by default, as a consequence of your choice to aim for a specific percentage of path coverage? For example, there are likely to many things that could be unit-tested for, but which could be ruled out as possibilities by tests at a higher level of abstraction. In that case, time spent on the unit tests could probably be better spent elsewhere, especially if (as with some examples from the article) a bug is not likely. 100% path coverage is one of those measures that are superficially attractive for their apparent objectivity and relative ease of measuring, but which don't actually tell you as much as they seem to. Additionally, in this case, the 100% part could be mistaken for a meaningful guarantee of something worthwhile.
- cowardlydragon 9y agoAlso: the fundamental undecidability of the Halting Problem
- candiodari 9y agoIf you actually know what the halting problem is, you know that if your code triggers that, it is pretty much broken. If you're not writing a programming language.
- dom0 9y ago... is really not relevant to software testing.
- vram22 9y agoPartly related: Is equivalence partitioning [1] used much in the industry in software testing? [1] https://en.wikipedia.org/wiki/Equivalence_partitioning https://en.wikipedia.org/wiki/Equivalence_partitioning I came across the concept some years ago, IIRC, in the classic book The Art of Software Testing by Glenford Myers [2], and used it in a few projects. But have not really heard or read of it being used much, from people or on forums. [2] https://en.wikipedia.org/wiki/Glenford_Myers https://en.wikipedia.org/wiki/Glenford_Myers I think it can be complementary to some of the other approaches mentioned in this thread, which, BTW, is interesting.
- apo 9y agoYou don't need to test that. ... The code is obvious. There are no conditionals, no loops, no transformations, nothing. The code is just a little bit of plain old glue code. Here's the code: @Override public void initialize(WatchlistDao watchlistDao) { watchlistDao.loadAll(watchListRow -> watchlists.add(watchListRow)); } Maybe I'm dense, but this code raises at least one question that I would prefer to see answered by tests. The parameter watchlists appears to be defined in a scope above the one under test. What happens if watchlists is null for some reason? What should be the behavior? Then there's the tricky question of what to do as this method evolves. Next month, a watchListRow might need to be updated with a value before being added to watchlists. Later, a check might be added to ensure some property exists on watchListRow. At what point will a test be written for this method?
- ishtu 9y ago>Testing is usually regarded as an important stage of the software development cycle. Testing will never be a substitute for reasoning. Testing may not be used as evidence of correctness for any but the most trivial of programs. Software engineers some times refer to "exhaustive" testing when in fact they mean "exhausting" testing. Tests are almost never exhaustive. Having lots of tests which give the right results may be reassuring but it can never be convincing. Rather than relying on testing we should be relying in reasoning. We should be relying on arguments which can convince the reader using logic. http://www.soc.napier.ac.uk/course-notes/sml/introfp.htm http://www.soc.napier.ac.uk/course-notes/sml/introfp.htm
- lowbloodsugar 9y agoI once joined a company that had 90% code coverage. After a while it became clear that there were all vanity tests: I could delete huge swathes of code with zero test failure. We let the contractors that wrote it move on, and we formed a solid team in house. We don't run code coverage any more because it makes the build run four times slower. Instead, I trust our teams to write the good tests. Sometimes that means <100% coverage, and the teams are able to justify it. Some feedback on the article: >Test-driven development, or as it used to be called: test-first approach Test-first is not the same as Test-Driven. The test-first approach includes situations where a QA dev writes 20 tests, and then hands them to an engineer who implements them. Thats not TDD. >"But my boss expects me to write test for all classes," he replied. That's very unlikely to be TDD. "Writing tests because I've been told to" is never likely to be "I'm writing the tests that I know to be necessary", and that's all TDD is: writing necessary tests. If the test isn't necessary, then neither is the code. >Look, if that imaginary evil/clueless developer comes and breaks that simple code, what do you think he will do if a related unit test breaks? He will just delete it. Sure. But then their name is on that act in the commit log. The test is a warning. I've been lucky not to have worked with evil developers, but I have worked with some clueless ones, and indeed some have just deleted tests. Thats an opportunity for education, and quality has steadily improved. >The tragedy is that once a "good practice" becomes mainstream we seem to forget how it came to be, what its benefits are, and most importantly, what the cost of using it is. Totally agree. So many programmers and teams practice cargo cult behaviors. Unfortunately, this article is one of them: making claims about TDD, and unit tests in general, without understanding "why" TDD is effective.
- Nemcue 9y ago> If the test isn't necessary, then neither is the code. How did you come to this conclusion? What if the code is just glue code for different libraries?
- raverbashing 9y agoMost of "we should go for 100% coverage" is simply cargo-culting (pushed by "gurus" like Uncle Bob - the negative aspects of the word guru implied) Not to mention 100% coverage is not guarantee the system works, in practice quite the opposite Not to mention this BDD crap which only makes my blood boil, it's syntactic yuck disguised as syntactic sugar
- BeetleB 9y ago>Most of "we should go for 100% coverage" is simply cargo-culting (pushed by "gurus" like Uncle Bob - the negative aspects of the word guru implied) I came here just to search for Uncle Bob to see the fun comments talking about him. No, he does not recommend 100% coverage. He is against writing tests for "basic" code like getters/setters. I can understand the irritation with Uncle Bob - especially if you've read his blog. But everyone seems to get his stance on unit tests wrong - including Uncle Bob fans. His fans say he advocates for 100% code coverage. His detractors say he advocates for 100% code coverage. All while Uncle Bob is saying he doesn't cover "trivial" code.
- knodi123 9y agoI recently broke a unit test by adding one entry to a hash constant (a list of acceptable mime types and their corresponding file extensions). I looked at the test, and it was just comparing the defined constant, to a hardcoded version of itself. I rewrote the test by converting the constant to a string, taking a checksum of it, and comparing _that_ to a short hardcoded value. Now the test is just 1 line of code, instead of 41! Then I put it through code review, and my team said "What a ridiculous test." But they didn't see any problem in the previous version that compared it to a 40-line hardcoded hash. It's a weird world.
- josteink 9y ago> The tragedy is that once a "good practice" becomes mainstream we seem to forget how it came to be, what its benefits are, and most importantly, what the cost of using it is. Totally agree. You can say this about lots of things really and not just tests.
- seabornleecn 9y agoI think pursuing 100% test coverage is not a fixed state, it is a must have process to learn how to write tests. Think about one question first: why did the manager force develop to achieve 100% coverage? There must have some benefits, or the manager might come from the competitor. When standing at a higher position, think of time and organization factors, it might be a good choice. If every engineer in the corporate has the deeply understanding of test coverage as the author, they really do not need to pursue 100% coverage. But in reality, we can see many companies which do not pursue test coverage, their coverage tend to be 0. That's why we need force 100% test coverage in a short time. Engineers need time to form the habit of test their code, and then experience the pain of bad tests. Then they start to think what kind of tests are valuable.