dominare il codice legacy
TRANSCRIPT
Dominare il codice ereditato
Tommaso Torti e Matteo Vaccari
Agile Day 2007, Bologna, 23 novembre
(cc) Some rights reserved.
ReadMePe r ve d e re f u n z i o n a re l ' a p p l i c a z i o n e : * m o d i f i c a / e t c / h o s t s i n s e re n d o 1 2 7 . 0 . 0 . 1 x x x . y y y. i t 1 0 . 0 . 1 . 2 x x x . z z z . i t
* i n s e r i s c i i s e g u e n t i p l u g i n d i F i re fox : * M o d i f i c a g l i h e a d e r : h t t p s : / / a d d o n s . m o z i l l a . o r g / e n - U S / f i r e f o x / a d d o n / 9 6 7 v a i s u To o l s - > M o d i f y H e a d e r s e a g g i u n g i : M S I S D N = 3 9 3 9 2 8 3 9 0 0 7 8 PA RT Y- I D = 3 4 3 5 3 2 5 2 * U s e r a g e n t sw i t c h e r : h t t p s : / / a d d o n s . m o z i l l a . o r g / e n - U S / f i r e f o x / a d d o n / 5 9
s a l v a re i l f i l e h t t p : / / x x x . s o u r c e s e n s e . c o m / f i l e s / z e r o 9 / u s e r a g e n t s w i t c h e r. x m l e i m p o r t a r l o
* e s e g u i " s c r i p t / c re a t e _ d a t a b a s e s . s h " * e s e g u i " s c r i p t / s t a r t . s h " * p u n t a i l b row s e r a h t t p : / / l o c a l h o s t : 8 0 8 0 / p r o g e t t o / p . d o ? p a g e = H o m e
create_databases.sh#!/bin/bashif [ ! -d db ]; then echo "Questo script deve essere eseguito nella dir principale del progetto" exit 1fiecho 'Drop databases...'mysqladmin -uroot --force drop dbmysqladmin -uroot --force drop db_test
echo 'Create databases...'mysqladmin -uroot create dbmysqladmin -uroot create db_testecho "grant all on db.* to db@localhost identified by 'db';" | mysql -urootecho "grant all on db_test.* to db@localhost identified by 'db';" | mysql -uroot
echo 'Build schema...'cat db/db-schema.sql | mysql -udb db -pdbcat db/db-schema.sql | mysql -udb db_test -pdb
echo 'Populate development...'mysql -udb -pdb db < db/populate_db.sql
echo 'Done!'
start.sh#!/bin/bashif [ -z "${CMT_DEVELOPMENT_UPLOAD}" ] ; then echo "Deve essere settata la variabile di ambiente CMT_DEVELOPMENT_UPLOAD"; exit 1;fi
ABS_PATH=$(cd $(dirname $0); cd ..; pwd)CATALINA_HOME="$ABS_PATH/tomcat-5.5.25"
rm -rf $CATALINA_HOME/logs/*rm -rf $CATALINA_HOME/webapps/progetto*
ant cleanant deploy
ln -s /tmp $CATALINA_HOME/webapps/progetto/dynamicImages/upload
$CATALINA_HOME/bin/catalina.sh jpda start
tail -f $CATALINA_HOME/logs/catalina.out
Log
Log
log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target=System.outlog4j.appender.stdout.layout= org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern= %5p %c{1}:%L - %m%nlog4j.rootLogger=INFO, stdout
Refactoring servletSpot the differences
Il dilemma
• Non posso rifattorizzare senza test
• Ma il codice è così ingarbugliato, che
• Non posso testare senza rifattorizzare
Il dilemma
• Non posso cambiare il codice senza testare
• Non posso testare senza cambiare il codice
L’algoritmo
• Trova il punto da modificare
• Rompi le dipendenze
• Scrivi i test
• Modifica; rifattorizza
Rompere le dipendenze
• Controfigure
• Cuciture
• Oggetti umili
Controfigure
• Come testare una servlet?
• Senza usare un web server?
• HttpServletRequest: 54 metodi!
Extract method
Sfrutta le cuciture
Test di integrazione
http://httpunit.sourceforge.net/
Refactoring servlet
Sensazioni
Tentazioni
xkcd.com
Test jsp @Test public void testHomePage() throws Exception { FakePageContext pc = new FakePageContext(); setRequestAttribute("DEVICECAPABILITIES", pc.capabilities);
get("/home.jsp");
output().shouldContain("NO-CACHE"); }
.... protected void request(String path, String httpMethod) throws Exception { JspCompiler compiler = JspCompilerFactory.newInstance(); compiler.setWebRoot(getWebRoot()); compiler.setOutputDirectory(getOutputDirectory()); Jsp jsp = compiler.compile(path, substituteTaglibs); execution = jsp.request(httpMethod, requestAttributes, sessionAttributes); }
http://sourceforge.net/projects/jsptest
Dipendenze
JavaGameRetriever BillingService
FakeBillingService
public JavaGameRetriever() { this(new BillingService()); }public JavaGameRetriever(BillingService billingService)
public class FakeBillingService extends BillingService
Configurazioni<target name="prepare"> <copy todir="./web/WEB-INF/"> <fileset dir="${conf.dir}"> <include name="ApplicationResource.properties"/> <include name="web.xml" /> </fileset>...
<target name="clean"> <delete file="./web/WEB-INF/ ApplicationResource.properties"/> <delete file="./web/WEB-INF/web.xml" />
createWarForPreProduction.sh
#!/bin/bashant cleanant war -Dconf.dir=conf/preproduction
RisultatiDopo Prima
...di tutto il progetto
RisultatiDopo Prima
...delle servlet
Risultati
> Coverage: 10 % su 26056 loc> Tempi: consegna on time - 7 settimane in un team di 3
Non perdere la testa
• Pianifica per feature
• Automatizza tutto
• Scrivi test
• Rifattorizza
• Lavora (interattivamente) il meno possibile!
(cc) Tommaso Torti & Matteo Vaccari 2007. Published in Italy. Attribuzione – Non commerciale – Condividi allo stesso modo 2.5