Ian Leitch wrote:
I'm just wondering what people consider to be a healthy code:test ratio (as
shown in rake stats).
Our new code base is currently 1:2, obviously not that great, what ratios do
you lot have?
I'm thinking 1:4 would be a nice ratio, though that's not a founded
I realize this isn't an accurate metric for analyzing test coverage, but
it's interesting at least.
1 line of code for every 2 lines of test is head-and-shoulders above the majority of software projects, including your competition's.
Let's assume you are using Test-Driven Development, where you make tests fail before writing code to pass them. (Otherwise even achieving 1:1 would be really hard!)
Pay attention to your test quality. If you frequently clone-and-modify test cases, and if the test cases get rather long and do many things, your code will indeed resist bugs, and you shouldn't spend much time debugging.
However, your tests won't be very DRY. This could account for a high test line count. Tests should be readable, individual, and self-documenting. If they do too many things, and if they have too many lines, there are some useful techniques you can use to merge them and make them more useful.
You could, for example, perform the "Extract Method Refactor" (look it up) to merge common assertion code into an application-specific assertion. If your application requires tied shoelaces, you could write 'assert_laces_tied(shoe)', for example. This improves the test's readability.
Next, many tests follow the "Execute Around Pattern". Look that up too - Ruby does it all the time. If your test creates a resource, then calls a tested method, then verifies that resource, you could encapsulate the "before" and "after" concepts, and put the actual test into a block. For example:
assert_laces_tied do |shoe|
Now you can write many test cases that create shoes, call different methods on them, and assert that their laces remain tied.
The next more advanced form of this concept is the "Abstract Test". That means you write two test suites as modules (not classes), and then you write some Test::Unit::TestCase classes that include these modules, and apply different setup() methods to them. Each setup() method creates a different object, and the inherited test cases check that they all behave the same. Abstract Test is a good way to enforce objects that obey the Liskov Substitution Principle.