An Approach to Code Coverage

My team at Microsoft is starting to use code coverage a little more diligently. Code coverage has been used for some time on the team, but we’re just now getting to a common approach and recording mechanism. There are a few things about our approach that are a bit different than I’ve seen most other people use, so I thought I’d share it here.

As a quick aside, we measure code coverage using block coverage (block coverage is similar to line coverage except that it groups continuous non-branching statements into a block for counting purposes). I’ve found that bullseye has a very good primer on code coverage measurement for those who are interested in more than I feel like writing about today.

A small group of us worked on the toolset, process and strategy for our team’s use of code coverage. One topic that came up was what target percentage of code coverage we should set as a goal. Years ago, when I was on the CE team, I helped build and deploy our code coverage toolset. Once our tools were working and we measured coverage, we set a goal for the next release (65% if I remember correctly). We cranked up the number a few percentage points each release, and I think we were approaching 80% by the time I left the team. As I type this, I remember something about making a promise to shave my head if we got higher than 80%. That’s only funny because about a year ago, I did shave my head (it’s since grown back).

That history is interesting, because I had been anticipating the question (target for code coverage percentage), and I was (and am) adamant about the code coverage goal I would like our team to shoot for.

I’m adamant that we have no goal for code coverage.

The problem of having a target goal for code coverage is that code coverage will improve – i.e. you get what you measure. Wait a minute – shouldn’t improving code coverage be a good thing? It is (for reasons I’ll explain lower), but here’s what usually happens in practice.

Let’s say I own three features – one major feature with high customer impact, one area with low customer impact, and one that’s somewhere in the middle. Now, let’s assume that my team has a goal of 70% code coverage (measured as an average across the components I own). I measure code coverage, and here are my results.

  Feature 1 – High Impact Feature 2 – Medium Impact Feature 3 – Low Impact
Code Coverage 70% 60% 50%

 

Now, if your goal is to get your average to 70% (or even if your goal is a minimum of 70%), you are going to put your testing efforts into testing medium and low impact areas. Shooting for a code coverage goal can convince testers to throw out their prior knowledge of risk and impact, and instead focus on “improving the number” – by testing stuff that probably doesn’t need more testing, while ignoring potentially important stuff. If you never measured code coverage in the first place, would you really spend time doing additional testing on the medium and low impact areas of the product (with more effort needed for the low impact area)?

It’s time to discuss the goal of measuring code coverage. It’s foolish to think that higher code coverage has anything at all to do with product quality. It only measures whether code is executed on at least one path. One of my common “Alan-isms” is this:

The only thing that 80% code coverage tells you is that 20% of your code is completely untested

What code coverage does do is point you to holes in your testing. The goal of measuring code coverage is helping you (as a tester) understand what is not being tested. Once you discover what’s not being tested, you can make a choice based on risk and impact on whether you need to add additional tests – or if your time is better spent elsewhere. By not having a percentage goal for code coverage, I hope the team can focus on improving tests and testing rather than improving a number.

By the way – you can replace the words “code coverage” with “test automation” above and tell a pretty similar story.

Another thing we’re starting to do is measuring code coverage on check-ins. We’re fairly late in the cycle now, so we want to be careful of regressions and ensure that all of the code coming into the product is well tested. What we do is filter our code coverage view to only look at changed lines of code in the changed list. Say we have a binary that has 10k blocks of code, but the latest checkin only changed (or added) 25 blocks. We can filter our testing and coverage on just those 25 blocks and ensure that we’ve at a minimum executed each line at least once during our initial testing. This helps a lot with regressions, as we can ensure that we look at error cases and other little used paths very close to the checkin rather than waiting until those errors pop up at a much later date.

This gives us the potential for 100% code coverage on every changed line of code.

But I would never make that a goal :}

Similar Posts

  • See Me…Hear Me

    Although I really enjoy talking about testing, I’m (purposely) speaking a lot less these days. I have a day-job that I love, and I like hanging out in the rainy Pacific Northwest. As of now (and I think a plan change is a long shot), I’m travelling to exactly one conference in CY13 (StarWest in…

  • Roles and Fluidity

    I had a twitter conversation this week about roles this week. I’ll recap it – and expand on my views; but first I’ll tell a story. Very early on in the Windows 98 project, I was performing some exploratory testing on the explorer shell and found an interesting (and slightly weird bug). At the end…

  • Stuff I Wrote

    I just put together a collection of my published works (it’s not a long list). I also have an article coming out in a Korean testing magazine – I’ll see if I can get a link once it’s out. I’ve been writing less lately while I turn my attention toward my often neglected day job….

  • Five for Friday – February 19, 2021

    Back again and sharing a few cool things I found this week. I wrote another article for TestProject – this one on why developers should write all (or at least most) of your automated tests. While I’m self-promoting, I’m giving a webinar next Wednesday on developer vs. tester mindsets. Former guest on the ABT 343…

  • Me, elsewhere

    I shared an article on rotten automation on the testproject blog here – https://blog.testproject.io/2017/11/07/somethings-rotten-test-automation/ A was also part of of Engel Jones 12-minute podcast series. My interview (very little about testing) is here – https://twelveminuteconvos.com/alan-page/  

7 Comments

  1. There a reason why you have chosen block coverage over other types as your guide of choice?

    [AP] – yes – our internal toolset measures block coverage (which is essentially the same as line coverage). For our purposes (discovering untested areas or conditions), it’s sufficient.

  2. Your Alan-ism: “The only thing that 80% code coverage tells you is that 20% of your code is completely untested”

    Does this ism not include manual testing performed on the project? Do you see any value in it?

    1. Of course I see value in manual testing – but you can track code coverage on manual testing just as easy as on automated testing – so of course, the ism applies to manual testing as well.

      Did I understand the question correctly?

      1. I think so. That’s all I need. Would love to hear how you check code coverage of manual testing, actually. Is that just “feature coverage” (break it up into logical blocks and test those)?

    1. 1) Launch VS command prompt

      2) Instrument the product dlls using vsinstr /coverage “…\abc.dll”

      3) Start coverage monitor by running the command vsperfcmd /start:coverage /output:”Test.coverage”

      4) Run your tests which are testing the functionality of “…\abc.dll” (manual or automated, doesnt matter)

      5) Stop the coverage monitor by running the command vsperfcmd /shutdown

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.