Brian Richardson

Coding, traveling, etc.

Testing Legacy Code

So the morning lecture I attended was more a general overview of project with legacy code switching to Agile, while the afternoon talk I attended was a tutorial on writing unit tests for existing production legacy code. This talk was excellent. It was by one of the “Beautiful Code” authors. He has a very practical approach to dealing with adding tests to existing legacy code.

The benefits of TDD are clear and 100% code coverage is always the goal. However, with existing legacy code you need to take a much different approach. When optimizing, or refactoring existing legacy code you can apply TDD and it works very well, however you must give up a few things:

  1. 100% Code Coverage
  2. Unit Tests

Broad Tests

Since you are writing tests for a system that has already been running on production for quite some time, you can assume for the most part it is offering what the user’s want, or there would have already been complaints and issues logged (there will be bugs, but overall it is functioning). So don’t focus on 100% code coverage or unit tests, instead focus on writing broad tests that confirm some functionality that the user relies on is working correctly.

Yes, when you make changes and a broad test does fail, it will not immediately show you the cause of the failure, like a typical unit test will. However, it will still show you that you have broken existing functionality and this is a big improvement over having no tests at all. In a small amount of tests we have been able to at the very least protect some existing functionality. The key is that since we usually have a very limited amount of time to devote to writing tests for legacy code, that it is more important to get broad coverage than targeted coverage.

Just like when using TDD with new functionality, when making any change at all in legacy code, ensure the tests are written BEFORE changing anything, but still in this case broad tests are the place to start. This is not advocating ignoring full coverage for your new code, only the legacy code. If you have time to write full unit tests for existing legacy code, great, but most developers do not. The key is to make sure your broad tests are in place FIRST.

When dealing with legacy code, the fastest gains in code coverage and also the biggest gains in reducing bugs come from starting your tests at the highest level possible. In writing these tests you do not think about the structure of the code, but rather just “What does this application do” and write tests to confirm that functionality and protect it.

Add a New Test and It Fails

Now in TDD when writing new code, the moment a test fails you fix the code until your test passes. However, when writing tests for existing legacy code, if you add a new test to an area that is not currently covered by tests and it fails, but you are confident the test should work, do not stop and fix the code to get the test to pass, this is a common mistake. You are far better off to log an issue for the bug and to continue to write more tests. The reason is because at this stage you do not have enough tests in place to ensure your bug fix will not break another area of the application. So write more tests, leave this test failing, and log an issue for fixing this bug.

Basic Method to Testing Legacy Code

So to get started with writing unit tests for legacy code you:

  1. Look at the existing application
  2. See what functionality it offers to the user
  3. Write tests to cover that high level functionality

Top Down Approach

So for writing unit tests for legacy code, you want to go in the order of what will give you the most gain for the least amount of effort, so go in the following order:

  • Tests for each Package
  • Tests for each Class
  • Tests for each Method (with legacy code, will probably never get this far)
  • Tests for each Line (100% code coverage)

Once again the Michael Feathers book “Working with Legacy Code” was highly recommended. I am amazed how highly regarded this book is as a reference for refactoring and getting code into a testable state.

March 7, 2008 Posted by | Uncategorized | Leave a Comment

How to be Agile with Fragile Legacy Code

I attended a talk today that had a different perspective on transitioning to Agile. Most discussions on Agile focus on teams in the initial stages of a project, not after several years of code has been written. In the short term the costs of adding legacy code appear cheap, but in the long term become increasingly expensive in terms of maintenance and QA costs. The basic approaches to reducing this cost later on, by investing in your code now is to reduce complexity, increase test coverage, introduce best practices (coding standards for both code and unit tests), and as a result reduce the number of issues that make it to the QA cycle and production.

Unit Testing (Adding tests to legacy code)

So where do you begin? Start by adding tests to cover existing functionality in your product. The product is stable and in production, so start covering it with tests. There are two types of tests

  1. High Level – these are the tests that ensure the system does what it is supposed to, protects the business logic. These tests allow us to confirm that the functionality the user expects from the system has not been broken by any additional changes. These tests cover the bugs that will be the most likely to occur
  2. Low Level – these are the tests that cover individual pieces of logic, the small units that make up the high level tests

You should start with high level tests, these are the tests that offer the most business value. It is important to capture the overall functionality of the system with tests first when dealing with legacy code, since these are the tests that will provide the least hindrances to future refactoring.

Teams need training on Unit Testing and often they demand it (especially when it comes to refactoring existing legacy code to get it into a testable state). Document it on the wiki and update it regularly!!!!! Providing this training early on prevents a mess that needs to be cleaned up later.

Switching to Agile

Switching an existing team with a legacy application over to Agile requires a different approach then starting Agile with a new team working on a new application. It is a mistake to take the “All or nothing” approach to SCRUM, the “We must do it all now”. Agile in this type of environment should be introduced in a slow incremental approach where each new process is brought in one at a time, refined in the retrospective, then as that process stabilizes another is introduced. So the cycle is “Start small, test, refine, add more”. This approach may feel like a slower change, but often can make the transition significantly easier. Switching a teams entire process all at once, can lead to frustration amongst the team and loosely followed processes.

Problems with Large Projects (Code size)

Often developers will change their development process just based on the size of the existing application. For example, you have a very large project, it is very slow in your IDE tool, you want to use a refactoring tool, but are unable to because of the size of your project and how slow the tool runs on the project. So what happens? The team does not use a refactoring tool. This is a mistake that misses focusing on the core issue: The project is too big, it needs to be broken up. It is also a hindrance to TDD. Quick feedback is extremely important for TDD, long build times and slow running unit tests end up hurting this process. Developers do not want to wait for the tests to finish, so will write more and test later.

Regular Software Cleanup

Make time in each sprint to not just write unit tests, but to also remove dead code and duplicate code as part of the step of adding those unit tests.

Testing and QA

Allow automation to happen over time, DO NOT force it. Establish the right processes first, everytime those processes get ironed out, QA will see common areas that can be easily automated, then you can move in that direction.

Piggy Backing

One approach discussed to introducing Agile methodologies into an existing team was called “Piggy Backing”. Do not introduce a new process all at once, just introduce new items to the existing process slowly overtime, piggy backing on what the team is already doing. The team will evolve to where you want them to be and often get their quicker, with far less pain and less resistance from the team

March 6, 2008 Posted by | Uncategorized | Leave a Comment

SD EXPO – Thursday Morning

This might well by my most difficult day in terms of deciding what lectures to attend. There are more lectures in each time slot that I want to attend, then any other day of the week. I have three lectures for each session marked off, and their are four time sessions today. This will not be easy ….

March 6, 2008 Posted by | Uncategorized | Leave a Comment

SCRUM

Mark and I attended a talk called “Principles and Practices of SCRUM” this afternoon. Mark will be blogging about the Burn Down and Burn Up chart views that were discussed. So I will stick to one main point that stood out.

When a team commits to completing a set of stories for a sprint, it is up to the Product Owners to constantly be viewing the Burn Down or Burn Up charts very early on in the sprint and check the velocity. If the velocity shows that the team will not be on track to finish all the stories that have been put in the sprint, it is extremely important to pull stories early in the sprint and not late. The reason is because when a team feels they have more work then they can handle they will rush to try and finish it all for the end of the sprint. This is bad because this is where many bugs get created. Pulling stories early on is far better for the team. It is better to under shoot and add extra items at the end of the sprint, then to over shoot and not complete items or have to pull items at the end of the sprint.

I thought this discussion was interesting and made a lot of sense.

March 6, 2008 Posted by | Uncategorized | 1 Comment

Refactoring Books

During the talks I have attended this week there have been some books that have been mentioned over and over again.

So far the number one recommended book, and this has been recommended by presenters who also have published excellent books on design and refactoring has been:

“Working Effectively With Legacy Code” by Michael Feathers

Also, other books mentioned repeatedly have been:

“Refactoring: Improving the Design of Existing Code” by Martin Fowler, Kent Beck, John Brant, and William Opdyke

“Refactoring to Patterns” by Joshua Kerievsky

March 6, 2008 Posted by | Uncategorized | Leave a Comment

Mastering Design Patterns

WOW. This talk was excellent. The room was packed with standing room only available in the back. Robert C. Martin went through a number of common design patterns. There is no reason to me trying to summarize them all, just get the book. In the talk we went over the design patterns:

  • Adapter
  • Strategy
  • Template
  • State (two level, and three level)
  • Observer
  • Model View Controller
  • Model Presenter
  • Command
  • Actor Model

This was an excellent talk.

http://www.objectmentor.com

Mastering Design Patterns

March 5, 2008 Posted by | Uncategorized | Leave a Comment

Brian Is Sad

A talk I wanted to go to this morning in the Design and Modelling track was cancelled. So I went with Mark to the “Better Test Design” talk on writing unit tests instead. Now it is lunch hour and I just found out the talk I wanted to attend this afternoon called “Prefactoring”, a presentation discussing many of the refactoring principles outlined in Martin Fowler’s book called “Refactoring”, was cancelled. I met the presenter last night, he seemed great, we chatted for a bit, to bad the presentation did not happen. So for my afternoon I will now be attending the sessions “Mastering Design Patterns” and “Principles and Practices of Scrum“. Mark and I had the chance to meet the presenter for this Scrum talk last night. It should be very interesting.

March 5, 2008 Posted by | Uncategorized | Leave a Comment

Some Cool Software

So while I was at the EXPO touring the software booths, I stopped by the Atlassian booth. This is the company that makes Jira and Confluence. They have a few other tools which look useful.

Fisheye (source repository viewer)

Crucible (code reviews)

March 5, 2008 Posted by | Uncategorized | Leave a Comment

TDD

During the talk on TDD (Test Driven Development) the loop of the TDD cycle was discussed. I realize some of you who have already read about this topic and are familiar with this cycle, but I just thought I would mention it.

The TDD Loop

  1. Write a small test for new functionality
  2. Write just enough code for the new functionality to pass the test
  3. Confirm that the revised system passes all tests
  4. Refactor to remove “code smells”
  5. Confirm that the revised system still passes all tests

So the question came up during the talk “How do you know how many tests to write?”. The answer to this question was “Write as many tests as you feel are necessary to feel confident that the code functions as it is supposed to”.  Developers are very good at writing positive tests (tests that show the code does what it is supposed to), however developers need to learn to get into the mindset of writing negative tests (tests that exercise conditions that should not happen but might).

TDD is extremely useful and important. Part of the problem pointed out with adoption is that if developers are just told to write unit tests and use TDD but are never given a good explanation of “Why” it can cause resistance to moving in this direction.

March 5, 2008 Posted by | Uncategorized | Leave a Comment

Unit Testing Private Methods?

I was not going to go into anything too specific in the blog, just stick to general concepts, but I do have to mention one interesting point discussed today. There was a section of the lecture devoted to unit testing and the topic of writing unit tests for private methods was mentioned. So the question was “Should you write unit tests for private methods” and the answer was NO.

Basically, a class has an interface of public methods. Those public methods use private methods. You write test cases for the public methods to ensure all functionality the class provides is covered by tests. If you need to write unit tests specifically for the private methods, then your test cases for the public methods are probably not covering all cases, and if they do, then the tests for the private methods are just duplicates of the public method test cases. One of the main problems with unit tests on private methods is that they make it difficult to refactor the inner workings of a class.

A developer should have the freedom to refactor the code within a class without needing re-write existing unit tests. We should be working at removing obstacles to refactoring and creating meaningful interfaces to our code that are both useful for development and testing.

March 5, 2008 Posted by | Uncategorized | Leave a Comment

Follow

Get every new post delivered to your Inbox.