Code Injection and Legacy CodeWhat's the best way of making the minimum changes to legacy code so you can test it?SO. You're lumbered into maintaining some old production code. It desperately needs refactoring, but you're in an environment where refactoring tasks are part of a project plan..."refactoring is seen as tinkering, and you might break something..." You know you need to test. The old code performs a
slow database query halfway through a vital method that your highly
suspicious of. The database query is fine. It's trivial and is used
elsewhere. What's the best way of making the minimum of changes to the code, and placating the permies at the same time? Here's my solution... |
The Old CodeHere's the old code. A unit test has been grafted in, highlighted in bold. It's a start, but it runs dog slow. |
|
using
System.Collections.Generic;
using
System.Threading;
using
NUnit.Framework;
namespace
InjectionClassLibrary1
{
public class
SuperAdderAnOldLegacyClass
{
private
readonly int
_portNumber;
public
SuperAdderAnOldLegacyClass(int
portNumber)
{
_portNumber = portNumber;
}
public
int DoAdd(List<int>
l)
{
var ret = 0;
// stuff happens
ret += DoSum(l);
// more stuff happens
return ret;
}
private
int DoSum(List<int>
l)
{
var ret = 0;
foreach (int
i in l)
{
ret += SlowSQLQuery(i);
}
return ret;
}
private
int SlowSQLQuery(int
i )
{
Thread.Sleep(1000 +
_portNumber/1000);
return i;
}
}
[TestFixture]
public
class
Container
{
[Test]
public
void OriginalImplemmentation()
{
var c =
new
SuperAdderAnOldLegacyClass(8080);
int sum = c.DoAdd(new
List<int>
{ 1, 2, 3 });
Assert.AreEqual(sum, 6);
}
}
} |
Minimum impact code has been grafted in,
highlighted in bold. |
|
using
System.Collections.Generic;
using
System.Threading;
using
NUnit.Framework;
namespace
InjectionClassLibrary1
{
public class
AdderAnOldLegacyClass
{
private
readonly int _portNumber;
public AdderAnOldLegacyClass(int
portNumber)
{
_portNumber = portNumber;
}
public
int DoAdd(List<int>
l)
{
var ret = 0;
// stuff happens
ret += DoSum(l);
// more stuff happens
return ret;
}
protected
virtual
int DoSum(List<int>
l)
{
var ret = 0;
foreach (int
i in l)
{
ret += SlowSQLQuery(i);
}
return ret;
}
private
int SlowSQLQuery(int i)
{
Thread.Sleep(1000 + _portNumber
/ 1000);
return i;
}
}
[TestFixture]
public class
Container
{
public
class SuperAdderMerelyForTesting
: AdderAnOldLegacyClass
{
public SuperAdderMerelyForTesting(int
portNumber) : base(portNumber)
{
}
protected override
int DoSum(List<int>
l)
{
var ret = 0;
foreach (var
i in l)
{
ret += i;
}
return ret;
}
}
[Test]
public
void FastTest()
{
var c =
new SuperAdderMerelyForTesting(8080);
int sumMock = c.DoAdd(new
List<int>
{ 1, 2, 3 });
Assert.AreEqual(sumMock, 6);
}
}
} |