Upload
jonas-folleso
View
1.494
Download
1
Tags:
Embed Size (px)
DESCRIPTION
Lightning talk from JavaZone 2010 on generating characterization tests for legacy code.
Citation preview
Generating Characterization Tests for Legacy Code
Jonas Follesø (@follesoe)
JavaZone 2010, 09. September
Huge methods (~3000+ lines)
Dav
e &
Kar
in h
ttp:
//w
ww
.flic
kr.c
om/p
hoto
s/dn
k_uk
/352
5103
502/
~50 slow integration tests
How the development team felt...
What they needed
What I read
Fras
er S
peirs
htt
p://
ww
w.fl
ickr
.com
/pho
tos/
fras
ersp
eirs
/339
5595
360/
Legacy code is code without tests. Code without tests is bad code.
-Michael C. Feathers
A characterization test is test that characterizes the actual behavior of a piece of code.
It acts as a change detector, protecting legacy code form unintended changes
public double Calc(double inv, double rt, int y){ double ret = 0; for (int i = 1; i <= y; i++) { ret = inv * Math.Pow(1.0 + rt / 100.0, i); } return ret;}
[TestMethod]public void Calc_characterization(){ var calc = new CalcUtil(); double result = calc.Calc(10000, 10, 10);
Assert.AreEqual(42.0, result); }
Assert.AreEqual failed. Expected:<42>.
Actual:<25937.424601>.
[TestMethod]public void Calc_characterization(){ var calc = new CalcUtil(); double result = calc.Calc(10000, 10, 10);
Assert.AreEqual(42, result); Assert.AreEqual(25937.424601, result); }
Test run completed. Results 1/1 passed.
public double CalculateCompoundInterest(double investment, double interest, int
years){ double projectedValue = 0.0; for (int year = 1; year <= years; year++) { projectedValue = investment *
Math.Pow(1.0 + interest / 100.0, year); }
return projectedValue;}
Fras
er S
peirs
htt
p://
ww
w.fl
ickr
.com
/pho
tos/
fras
ersp
eirs
/339
5599
536/
…A pinch point is a natural encapsulation boundary. When you find a pinch point, you’ve found a narrow funnel for all the effects of a large piece of code…
-Michael C. Feathers
~85% code coverage
http:
//w
ww
.flic
kr.c
om/p
hoto
s/sh
utter
cat7
/713
1862
11/
// This method is the "pinch point" we want to test...public object CalculateSomething(object someParameter){ // 1) Record input parameters... // 2) Do the work... // 3) Write parameters and return value to XML file.}
[TestMethod]public void CalculateSomething_test1(){ Run("Recordings/CalculateSomething1.xml");}
[TestMethod]public void CalculateSomething_test2(){ Run("Recordings/CalculateSomething2.xml");}
public void Run(string filename){ // Load input parameters from XML file // Load return value from XML file // Call method under test // Use reflection to compare actual vs. recorded}
~300 fast characterization tests
How can we reuse this?
http://follesoe.github.com/BlackBoxRecorder
[Recording]
[Dependency]
[Recording]public List<EmployeeEntity> GetMakingMoreThan(double salary){ var dal = new EmployeeDAL();
var employees = dal.GetAllEmployees();
return employees.Where(e => e.Salary > salary).ToList();}
[Dependency]public class EmployeeDAL{ public List<EmployeeEntity> GetAllEmployees() { // Calls to database... }}
<Recording> <Name>GetEmployeesMakingMoreThan_salary</Name> <Method>GetEmployeesMakingMoreThan</Method> <Type><![CDATA[BlackBox.Demo.App.SimpleAnemic.EmployeeBL]]></Type> <InputParameters> <Parameter> <Name>salary</Name> <Type><![CDATA[System.Double]]></Type> <Value><![CDATA[5000]]></Value> </Parameter> </InputParameters>
<Return> <Type><![CDATA[List<BlackBox.Demo.App.SimpleAnemic.EmployeeEntity>]]></Type> <Value><![CDATA[XML representation of employees retruned from method]]></Value> </Return>
<Dependencies> <Dependency> <Type><![CDATA[BlackBox.Demo.App.SimpleAnemic.EmployeeDAL]]></Type> <Method> <Name>GetAllEmployees</Name> <ReturnValues> <ReturnValue> <Value><![CDATA[XML representation of all employees returned from db]]><Value> </ReturnValue> </ReturnValues> </Method> </Dependency> </Dependencies>
</Recording>
[TestMethod]public void GetEmployeesMakingMoreThan_salary(){ Run(@"GetEmployeesMakingMoreThan_salary.xml");}
http://github.com/oc/jackbox
@Recordingpublic int exampleMethod(int parameter, int parameter2) { return parameter + parameter2;}
@Dependencypublic String invokedMethodOnDependency(String argument) { return argument.toUpperCase();}
BEKK CONSULTING ASSKUR 39, VIPPETANGEN. P.O. BOX 134 SENTRUM, 0102 OSLO, NORWAY.
WWW.BEKK.NO
Jonas FollesøSenior Consultant
+47 977 [email protected] /