Cracking open a legacy code 'black box'...
Legacy code and integrations can seem black box when you see them for the first time. You’re expected to get familiar quickly. No matter how much time you’re given to get familiar with your new project, you could always use more time.
The first time this hit me in the face was when I was given a 2-day deadline to add a feature to a legacy project. It seemed simple enough so I agreed that I could do it. When I opened the project, I felt sick. It was in VB.NET. I had spent years learning C#. C# was the language I was supposed to be using for everything. How could I ship something in VB.NET in 2-days?
As a C# developer with a deadline, a VB.NET method looks intimidating:
Public Shared Function WordWrap(ByVal input As String,
ByVal len As Integer,
ByVal delimiter As Integer) As String
' If the input String is less than the lenght specified, no delimitors
' are necessary.
Dim strBuilder As String = String.Empty
Dim seps() As Char = {" ", "\n"}
Dim words() As String = input.Split(seps)
Dim curLineLength As Integer = 0
Dim word As String
For Each cword As String In words
word = cword.Trim()
If curLineLength + word.Length > len Then
If curLineLength > 0 Then
strBuilder = strBuilder + Chr(delimiter).ToString
curLineLength = 0
End If
While (word.Length > len)
strBuilder = strBuilder + word.Substring(0, len - 1) + "-"
word = word.Substring(len - 1)
strBuilder = strBuilder + Chr(delimiter).ToString
End While
word = word.TrimStart()
End If
If curLineLength > 0 Then
word = " " + word
End If
strBuilder = strBuilder + word
curLineLength += word.Length
Next
Return strBuilder
End Function
There were many more methods like this, looking entirely foreign to me. There was no way I was going to learn the language in 2 days.
I found a better solution… tests.
Since VB.NET and C# both compile with the CLR down to the same machine language, I can write tests for VB.NET code in C#. If I can write tests for the VB.NET code, I will only have to learn the language features I need in order to get the work done.
I started with a first test, just trying to run the code… trying to get a first test to pass:
[Fact(DisplayName = "What does WordWrap do?")]
public void What_does_wordWrap_do() {
var input = "This is a string I'm going to use as input just to see what this method does";
var output = CommonUtilities.WordWrap(input, 10, 1);
Console.WriteLine(output);
}
Here’s the stdout:
This is a\x1string I'm\x1going to\x1use as\x1input just\x1to see what\x1this method\x1does
So this method puts a character at every len
value in a string. Looking at the coverage of the test, looks like I did pretty well:
Notice we didn’t cover all of the method… there’s branching logic that the original developer had to add - either because a) he anticipated it or b) he ran into a bug caused by not having that logic.
So I’ll cover it:
[Fact(DisplayName = "When WordWrap input has a string that is longer than the len parameter")]
public void WordWrap_when_string_is_larger_than_len_value() {
var input = "Thisisasuperlongstring";
var output = CommonUtilities.WordWrap(input, 10, 1);
Console.WriteLine(output);
}
Here’s what passing in a word that’s larger than the len
variable does:
Thisisasu-\x2perlongst-\x2ring
The method adds a “-” to break the string up if it exceeds the len
parameter.
That was just a simple example, but what if you’re needing to look at methods with an endless number of parameters and 100s of lines of code?
Public Shared Sub BulkWrite(ByVal RecordId As String,
ByVal session As UniSession,
ByVal uFile As UniFile,
ByVal writeType As String,
ByVal sqh As SalesQuote,
ByVal numberOfDetails As Integer,
ByRef writer As LogWriter)
// 300 lines...
End Sub
By taking the same incremental testing approach as I have above, I would be able to get a handle on what this code is doing, without even knowing VB.NET.
You can use tests as a tool for working with legacy code. Testing is more than just making sure your code doesn’t break when you push it to your build server or production. When applied to legacy code, tests can reveal what’s going on inside of “black boxes.” This is true even when the production code is written in a language you’ve never seen before.
I have more to talk about regarding testing, so make sure you sign up for my newsletter to get these posts sent right to your inbox!
Related Posts:
- Debugging all the time isn’t fun…
- New to Testing Time-Dependent Code?… Use NodaTime.Testing!
- Converting DateTimes by Offsets with NodaTime
- Intuitive Testing with Legacy Code
- How to Compare Object Instances in your Unit Tests Quickly and Easily
- Should You Jump into Unit Testing before OOP?
- How Working the ‘Vertical Slice’ Can Fix your Coding Mental Block
- Using TDD to Break Through ‘Paralysis by Analysis’
- Do I Really Have to Unit Test This Method?