Code coverage is either a key metric or a terrible distraction depending on who you ask. We favor code coverage as a useful indicator but not the only indicator.
At its core, code coverage is about measuring how much of the code is exercised when tests are executed. The goal is to indicate whether the tests are rigorous enough to validate the piece of software. Low coverage? Something isn't being tested. High coverage? The code might be thoroughly tested. The key here is the "might".
High code coverage can occur without good tests. There are a variety of ways to achieve high code coverage but provide little indication as to whether the code is valid. Does a test call a method yet verifies nothing about the method's output? This will still generate good coverage but is nearly useless in validating the code. Is a class untested, but still called by other classes? This will still generate coverage. Like any metric there are ways to game the system. It's one tool in a suite of tools.
We use jUnit for Java unit testing and Mockito to handle test mocks. When developing with the Eclipse IDE, we use the EclEmma code coverage plugin. The IntelliJ IDEA IDE includes pretty convenient code coverage features.
The IDE plugins give us an idea if there are untested areas while developing. Personally, I run coverage tools towards the end of testing to validate I didn't miss something. It's a great step before creating a pull request.
Our goal for Java modules is over 80% code coverage. I find 80% is a good compromise between thorough testing, development cost, and usefulness to find bugs. Full 100% coverage is expensive; gaining the last few percentage points often takes complex tests to reach every branch. Those complex tests then become their own problem to keep updated. We save 100% coverage for key code that needs it. Crucial algorithms and classes require the highest level of validation. Java Beans might receive less when it only contains simple getters and setters. We do have many complex classes with 100% coverage.
Code that is difficult to test usually indicates an design issue. If a class is difficult to test, we will refactor the class so it is testable. This could be either because the design could be more modular or because the code is unnecessary. With the former, the class can be refactored into multiple classes or methods with a more narrow scope. With the latter, the code can be removed. I find it satisfying to remove unnecessary code and it's a quick way to increase coverage.
Code reviews are our number one way to weed out "tests" that increase coverage but don't add value. Tests are reviewed as part of the pull request process. Code smells include tests without assertions or new classes without tests at all (that's a clear one).
At Collective Scientific we use code coverage as one tool to indicate code quality. Tests and coverage help us identify design improvements. Code reviews help us maintain quality test suites.
Also published on Medium