2007-08-24

How do you achieve 100% test coverage?

When doing Test-Driven Development, one critical metric is test coverage. And it is often quite difficult to achieve 100% test coverage on a given method using its "normal" unit tests.

This can often lead to very strange looking test code written specifically to test all possible branches and every corner case. Sometimes this is useful, but more often than not its testing for testings sake.

But if your test suite is good enough and tests all the designed behavior, uncovered code is a good indication of unused code. Try to delete it. If it still compiles and all tests pass, the code was dead weight code you can safely remove.

This is specifically often true for getters and setters for each and every attribute on a particular class. Put YAGNI (You Ain't Gonna Need It) to use and remove all setters and getters not in active use by your code. If you need one later, you could always add it. This makes the code much more compact and easier to read.

But what if the getters and setters are part of an API? Well, then the API should be an interface, shouldn't it?

8 kommentarer:

Andres Almiray sa...

Well, following TDD to the letter (very hard to do at first) you will always have 100% coverage because not a single line of production code should be written that doesn't respond a question asked by the test code. Meaning that if you follow TDD and have less than 100% coverage you are doing more than needed, effectively not applying DRY or YAGNI. And remember that 100% coverage is not an useful metric in itself, please check Andy's blog for more pointers (http://thediscoblog.com) =)

Asgeir S. Nilsen sa...

Andres, thanks for your feedback and the link.

In a perfect world where you get to start from scratch and apply TDD principles from day one, code coverage should never be an issue.

However, if you are introducing TDD later in a project or is new to the methodology, using coverage to measure your progress and the state of your tests is very useful.

Stephan sa...

Reaching 100% test coverage is possible, but seldom desirable.

The hardest code to reach is certain methods in your Exceptions and every catch clause in your try/catch constructs. Letting yout mocks throw exceptions help here.

Parts of your GUI layer (Swing/Web) can be hard to cover too, as can be your DB code.

I think desired test coverage depends on the project and should range between 70 and 95 %.
Below 70 and your not testing every important aspect, above 95 and your putting to much energy into testing.

Some tips for testing though: HSQL can help with DB testing, the automatic 'sa' user and the automatic DB creation on the fly in memory make it easier. Wiser SMPT helps with mail testing, Apache virtual file system helps with testing the reading and writing of files (e.g. configuration). Jemmy together with the view/editor patterns helps to reach high coverage for Swing.

Peace
-stephan

--
Stephan Schmidt :: stephan@reposita.org
Reposita Open Source - Monitor your software development
http://www.reposita.org
Blog at http://stephan.reposita.org - No signal. No noise.

Juriy sa...

Nice post, thanks.

I'm new to a TDD, still I feel I need to implement this approach in my project.

I've got a question: is it always possible to achieve 100% coverage? For example, if I'm designing or customizing swing component, what should I test?

Speaking about swing and UI, what approaches can you recommend to test those?

Asgeir S. Nilsen sa...

juriy,

In general when driving your development through tests, you write tests for the code you intend to implement. Take a look at the Test Driven Development Cycle.

You should also be very specific on what you are testing, and even more specific on what you are not testing. When designing Swing components, you write tests for that component, and not Swing itself.

When writing a GUI using swing, you test your use of the Swing API, and not Swing itself. As Stephan mentioned, Jemmy can be of help here.

When unit testing your DB code, you are not testing the JDBC driver or the database implementation. In-memory databases or mock JDBC drivers can help, but also mocking the database access API you are using.

I use Spring JDBC for one project, and mock out the JdbcOperations and SimpleJdbcOperations interfaces using EasyMock.

Dushan Hanuska sa...

Nice post!

Just remember that 100% coverage does not necessarily mean that the code is well written and 100% correct.

Code coverage only indicates the percentage of code that was exercised during the test run.

covered != tested

To make it fully (100%) tested one would need to write tests that cover edge cases as well as the common scenarios. This would usually involve multiple runs over the same code paths. Code coverage can definitely help here. I would only use it as an aiding tool to writing tests, not as an indicator of test quality.

Asgeir S. Nilsen sa...

Dushan,

Thanks!

When you design test code, you should be as thoughtful and considerate as when you design production code. All the practices of good design apply to test code as well.

Juriy sa...

Asgeir, thanks for your reply, I'll examine resources you supposed.

Still my question is a bit different. Say, I'm implementing a custom component: a translucent button.

I can easily evaluate that button is _actually_ translucent just looking at the interface. But how am I supposed to check that automatically within the unit test?