SpringOne Americas

Private Events

Blogs

View all Blogs >>
  • Howard Lewis Ship

    Creator of Tapestry and HiveMind

    It took a lot of ferreting out, but I eventually learned that you can put a <version/> tag in your site.xml, and Maven will put the... more»

  • Brian Pontarelli

    Founder of Inversoft

    Just figured out how to get git tab completion working in zsh on a Mac. Turns out that the completion scripts use a bunch of extra git... more»

  • Stuart Halloway

    CEO of Relevance

    Steve Yegge's most recent post takes more»

  • Mike Levin

    Software Developer specializing in Web2.0 websites

    more»

  • Erik Doernenburg

    Principal Consultant @ Thoughtworks

    If you are somebody who writes code you probably know that moment when you look at some code you didn’t write, or some code you wrote a... more»

  • Kirk Knoernschild

    Software Developer & Mentor

    more»

  • Richard Monson-Haefel

    VP of Developer Relations, Curl Inc.

    more»

  • Alex Miller

    Sr. Engineer with Terracotta Inc.

    Start your engines kids - the JavaOne 2009 more»

  • Brian Goetz

    Author of Java Concurrency in Practice

    I live in an AT&T-free state, so I have not had access to the cult that is iPhone. But recently, in preparation for AT&T moving... more»

  • Matthew Bass

    Software Developer & Entrepreneur

    Can Sphinx and foxy fixtures place nicely together? Due to the way Sphinx indexing works, foxy fixtures will often slow down the indexing... more»

  • Jason Rudolph

    Author of Getting Started with Grails

    I had the more»

  • Ryan Shriver

    Business and Technology Consulting

    more»

  • Neal Ford

    Application Architect at ThoughtWorks, Inc.

    One of the techniques I more»

  • Michael Nygard

    Agile technology leader and dynamicist

    Patrick Muellr has an interesting post about being more»

  • Nathaniel Schutta

    Author, speaker, software engineer focused on user interface design.

    Today we learned something important, the NTSB announced the more»

  • Jeff Brown

    SpringSource Engineering And Professional Services - Groovy and Grails Developer

    Strange enough title.Let's start with a hypothetical conversation between a geeky developer and his much less geeky wife: more»

  • Ted Neward

    Enterprise, Virtual Machine and Language Wonk

    ... Corey Vidal, you have outdone every YouTube video I've ever seen, and I was a hug more»

  • Pratik Patel

    Enterprise Architect

    SpringSource today announced that it has purchased G2One, the folks who have been driving the development of Groovy and Grails. The following... more»

  • Graeme Rocher

    Project Lead of the Grails Project & CTO of G2One

    You may have already read about it in the various news outlets and blogs covering the announcement, but if you haven’t I’m excited to spread... more»

  • Andrew Glover

    Co-author of "Continuous Integration"

    more»

  • Matt Raible

    Creator of AppFuse and author of Spring Live

    This morning, my co-workers and I discovered that Link edIn decided to trim 10% of its employees. The Denver Offi more»

  • Jared Richardson

    Agile coach and co-author of Ship It

    Jurgen Appelo has an ongoing interview series on his blog. He's published a lot of very smart people and I'm honored to squeak in too! ;) more»

  • David Bock

    Principal Consultant, CodeSherpas Inc.

    I have been setting up a rock-solid server cluster for a client and ran into an interesting issue trying to install Phusion Passenger onto... more»

  • Pramod Sadalage

    Co-author of "Refactoring Databases:Evolutionary Database Development"

    Consider this Hibernate mapping @Column(name = "qReferenceId") public Long getQReferenceId() { return qReferenceId; more»

  • Craig Walls

    Author of Spring in Action

    At one time not too long ago, I wasn't a big fan of annotations. But then I let my guard down and even started liking them. But now I'm... more»

  • Scott Leberknight

    Chief Architect at Near Infinity

    In late 2006 Neal Ford wrote about Polyglot Programming and predicted more»

  • Kenneth Kousen

    President of Kousen IT, Inc.

    In this entry in my “Making Swing Groovy” series, I want to talk about threading issues. Specifically, more»

  • Venkat Subramaniam

    Founder of Agile Developer, Inc.

    I wrote a four part article for Java World on creating DSLs in Java and Groovy. For your convenience, I decided to list the links to those... more»

  • Jason Harwig

    Senior Software Engineer at Near Infinity

    The most popular entry I've written at Near Infinity has been the more»

  • John Heintz

    Principal Consultant with New Aspects of Software

    In a recent discussion interview questions came up, here's my favorite one.To set some context this question is designed to gauge the abst more»

  • Mark Johnson

    Director of Consulting at CGI

    At the Columbus NFJS show held on July 25-27th during one of the BOF sessions Dave Bock, Scott Davis and I discussed unit tests vs functional... more»

  • Joseph Nusairat

    Author of Beginning JBoss Seam & Co-Author of Beginning Groovy & Grails

    Well i am assuming Apress has the most random site in the world at times.But today only they have our recent book, Beginning Groovy & Grai more»

  • Keith Donald

    Lead of Spring Web and Creator of Spring Web Flow

    I am pleased to announce that Developing Rich Web Applications with Spring, a three-day bootcamp lead by SpringSource engineers on web... more»

  • Vladimir Vivien

    Software Engineer / Consultant

    Judging from the list of features that will be included in NetBeans 6.5, more»

  • Pete Behrens

    Organizational Agility Coach

    Marti nig & Associates Methods & Tools group recentl more»

  • Brian Sam-Bodden

    Java author, Ruby geek and Open Source Advocate

    In this installment we are going to build the Dashboard page of the Tempo application. T more»

  • Mark Fisher

    Spring Integration Lead

    In my recent post, I had mentio more»

  • Ron Bodkin

    Chief Software Architect, Quantcast

    I'm looking forward to speaking at The Rich Web Experience conference in San Jose next month. The event runs from September 7th through 9th.... more»

  • Mark Goodwin

    Web Application Security Specialist

    We've already looked at one of the two big problems posed by anti DNS pinning on Java applets; because there's rebinding on the applet and... more»

  • Scott Davis

    Author of "Groovy Recipes" & TDD Expert

    Every time I see a live show at the Denver Botanic more»

  • Romain Guy

    Java User Interface expert.

    more»

  • Ramnivas Laddad

    Author of AspectJ in Action, Principal at SpringSource

    InfoQ.com has published my AOP myths and realities talk recorded at a No Fluff Just Stuff conference. InfoQ.com founded by Floyd Marine more»

  • David Geary

    Author of Graphic Java and co-author of Core JSF

    The 2006 NFJS tour kicked off t more»

  • Kito Mann

    Editor-in-chief of JSF Central and the author of JSF in Action

    One of the enhancements added for JSF 1.2 was update of the MethodExpression signature for the action attribute of h:command{Link,Button}. In... more»

  • Jason Hunter

    Author of Java Servlet Programming

    I just posted the JDOM 1.1 release for download. This release includes about 20 improvements and bug fixes. more»

Testing Anti-Patterns: Incidental Coverage

Posted by: Jason Rudolph on 06/17/2008

So you’ve taken your project to 100% code coverage, you’ve configured your continuous integration system to fail the build if that coverage ever drops below 100%, and you’re ready to enjoy the fearless refactoring and the rock solid regression testing suite that your software engineering rigor has now earned you. But are you really covered? What does 100% code coverage mean in your project? Is it enough to know that your test suite encounters every line of code? Or don’t you want to be sure that it exercises every line? If you simply encounter the line without asserting that it produces the correct results, are you any better off? [1]

Failure to Assert

Just achieving 100% code coverage is the easy part. Making it mean something: that’s where the real value kicks in. Consider the ease with which we can get to 100% line coverage on the following code (generated using Rails 2.1 scaffolding).

  1. class ProductsController < ApplicationController
  2.   # GET /products
  3.   # GET /products.xml
  4.   def index
  5.     @products = Product.find(:all)
  6.  
  7.     respond_to do |format|
  8.       format.html # index.html.erb
  9.       format.xml  { render :xml => @products }
  10.     end
  11.   end
  12.  
  13.   # … remaining methods omitted
  14. end


In order to ensure that the #index method is performing all its proper duties, we’ll define the following “test case.”

  1. require File.dirname(__FILE__) + ‘/../test_helper’
  2.  
  3. class ProductsControllerTest < ActionController::TestCase
  4.   def test_should_get_index
  5.     get :index
  6.   end
  7.  
  8.   # … remaining tests omitted
  9. end


We’ll use rcov to assess the results.

Example 1

And just like that, we have 100% code coverage for the #index method. [2] In this case though, that clearly means nothing more than the fact that we encountered 100% of the lines in the method. When code coverage is that easily achieved, it hardly seems cause for celebration. To get any real value from the test, we need to actually assert that we’re getting the expected results. Until we do so, we have nothing more than incidental coverage. [3]

The test code provided by the Rails scaffolding gets us closer to where we want to be.

  1. require File.dirname(__FILE__) + ‘/../test_helper’
  2.  
  3. class ProductsControllerTest < ActionController::TestCase
  4.   def test_should_get_index
  5.     get :index
  6.     assert_response :success
  7.     assert_not_nil assigns(:products)
  8.   end
  9.  
  10.   # … remaining tests omitted
  11. end


The tests pass, and our code coverage is still at 100%. At this point we’ve significantly increased the value of that particular test. No longer does the code have to crash spectacularly in order to yield a test error. Instead, exiting the method with anything other than a success response code will result in a test failure. Similarly, failure to set the @products instance variable will cause the test case to flunk. [4]

100% Covered. 50% Tested.

But while we’ve improved on the initial test case, at best we’re really only halfway to where we want to be. What would our test suite tell us if we were to alter line 9 as follows.

  1. class ProductsController < ApplicationController
  2.   # GET /products
  3.   # GET /products.xml
  4.   def index
  5.     @products = Product.find(:all)
  6.  
  7.     respond_to do |format|
  8.       format.html # index.html.erb
  9.       format.xml  { raise :fatal_error }
  10.     end
  11.   end
  12.  
  13.   # … remaining methods omitted
  14. end


  1. $ ruby products_controller_test.rb
  2. Loaded suite products_controller_test
  3. Started
  4. ……..
  5. Finished in 0.17716 seconds.
  6.  
  7. 8 tests, 15 assertions, 0 failures, 0 errors


Unfortunately, our test suite still blindly gives a thumbs up, despite the fact that any attempt to access the XML-formatted output would yield an exception. While our test suite includes assertions for how the application should prepare an HTML-bound response, our coverage of the XML-specific logic in line 9 remains merely incidental.

If we want our automated test suite to ensure that the #index method remains capable of producing an XML-formatted response as our codebase changes over time, we’d need to add a test case to exercise that code.

  1. class ProductsControllerTest < ActionController::TestCase
  2.   def test_should_get_index_formatted_for_html
  3.     get :index
  4.     assert_response :success
  5.     assert_not_nil assigns(:products)
  6.   end
  7.  
  8.   def test_should_get_index_formatted_for_xml
  9.     @request.env[‘HTTP_ACCEPT’] = ‘application/xml’
  10.     get :index
  11.     assert_response :success
  12.     assert_not_nil assigns(:products)
  13.   end
  14.  
  15.   # … remaining tests omitted
  16. end


rcov doesn’t reward us with any extra credit for writing this test case, but achieving 100% coverage is not the primary goal of a good test suite. [5] The primary goal is to ensure that the code satisfies the requirements. If the application really is required to provide an XML-formatted list of products, then we should seriously consider defining a test for that functionality in our test suite. [6]

Size Matters

In the examples above, we started off with a 5-line method that had 100% line coverage but lacked any assertions. That scenario provides some insight into other places where we might find a high percentage of incidental coverage. While long methods are a bad idea in general, the simple act of invoking a method may be enough to register it as having 100% coverage. If our test suite results in the execution of a 25-line method with no branches, all 25 lines are considered to be covered, regardless of whether we perform any assertions to verify the results of those 25 lines. Even if we refactor the code into smaller methods, if we don’t unit test those new methods, we’re still left with nothing more than incidental coverage.

Use It Wisely

If your goal is to achieve 100% coverage, then incidental coverage is your friend, but your test suite will fall far short of its full potential. You’ll run the risk of acquiring a false sense of security, and the ability to safely and fearlessly refactor is out the window. Changes or enhancements to your application will occur without the safety net of a full regression suite.

If your goal is instead to develop a comprehensive test suite to A) validate your application’s functionality and B) ensure that you build just enough software, then test-driven development (TDD) is your friend (as are peer reviews and/or pair programming). And subsequently, you’ll enjoy the nice side effect of 100% code coverage, because you’ll only build the code necessary to satisfy your tests.

Notes

[1] Sure, you know that the line executes without causing the application to crash (at least for this set of inputs), but does a lack of crashing really constitute success? Not likely.

[2] While we have 100% line coverage for the #index method, we don’t have 100% coverage for the ProductsController class as a whole. As the full coverage report shows, the tests provided with the Rails scaffolding do not cover the exception cases in the #create and #update methods.

[3] Incidental coverage is just as valuable as that guy that puts in “face time” at the office. He’s physically present at least 8 hours every day. People see him there. He’s at the office. He must be doing something. Right?

[4] At this point we’ve gone from covering nothing to covering something. That’s good, but we should ask ourselves whether we’re performing the most appropriate assertions for this test case. For example, is it enough to assert that assigns(:products) is not nil, or should we be making a stronger assertion about its value? Do we want to ensure that we’re rendering the intended view templates? What about verifying the content of the HTML (or XML) that gets rendered? Tackling issues specific to testing Rails controllers would take away from the focus of this post, but the strength of assertions and the layers at which we test is surely fodder for future posts.

[5] However, with anything less than 100% coverage, fearless refactoring and full regression testing simply isn’t something your test suite can provide. In Software Testing Techniques (1996), Boris Beizer takes the hardline on anyone that would consider developing a codebase without at least 100% line coverage: “[Line coverage] is the weakest measure in the family [of structural coverage criteria]: testing less than this for new software is unconscionable …”

[6] You could argue that this new test case is too similar to the original test case, that it needs stronger assertions, or that the current functionality is too simple for this new test case to offer sufficient benefit, but it’s up to you to assess how important automated testing is of that code. If you opt to not test your application’s ability to provide an XML-formatted list of products, you must not allow your 100% line coverage to give you a false sense of security about your code. That code may be covered, but it’s not tested.


This series is taken from the How To Fail With 100% Test Coverage talk. Check the schedule for a talk near you.

Thanks to Stuart Halloway for reading drafts of this post.


be the first to rate this blog


About Jason Rudolph

Jason Rudolph is a Principal at Relevance, a leading development and training organization specializing in Ruby, Rails, Groovy, and Grails, and integrating them into enterprise environments. Jason has more than nine years of experience in developing software solutions for domestic and international clients of all sizes, including start-ups, Dow 30 companies, and government organizations.

Jason is the author of the highly-praised book, Getting Started with Grails, and speaks frequently at software conferences and user groups. Jason also contributes regularly to the open source community, both as an early committer to Grails, and also as a committer to the Streamlined framework and numerous other Ruby and Rails projects.

Jason holds a degree in Computer Science from the University of Virginia. You can find Jason online at http://jasonrudolph.com.