Tải bản đầy đủ - 0 (trang)
Chapter 15. Performance Testing with JUnitPerf

Chapter 15. Performance Testing with JUnitPerf

Tải bản đầy đủ - 0trang

yourordinaryunittests,inordertoavoidpenalizingthefast

feedbackcyclethatisoneofthetrademarksofgoodunittests.

Notethatwearetalkingaboutverifyingperformance,not

optimizingcodeinanuncontrolledmanner.Premature

optimizationhasoftenbeendecried,andwithsome

justification.TonyHoareisfrequentlyquotedassaying,"We

shouldforgetaboutsmallefficiencies,sayabout97percentof

thetime:prematureoptimizationistherootofallevil."

Optimizationshouldindeedbeaveryfocusedtask,withprecise

goals.Itisafutileexercisetooptimizecodethatishardlyever

used.However,makingsureyourapplicationperformscorrectly

whereitneedsto,rightfromthestart,canbeahugetimesaver.And,byformalizingtheprocessandincorporatingthe

testsintotheapplicationtestsuites,verifyingyourapplication's

performancewithJUnitPerfcanhelptoencourageamore

systematicapproachtooptimization.Ratherthanoptimizingfor

thesakeofoptimizing,youmeasuretheperformanceyouhave

andcheckitagainstwhatyouneed.Onlythendoyoudecideif

optimizationisnecessary.



Chapter15.PerformanceTestingwith

JUnitPerf

IntroducingJUnitPerf

MeasuringPerformancewithTimedTests

SimulatingLoadwithLoadTests

Load-TestingTestsThatAreNotThread-Safe

SeparatingPerformanceTestsfromUnitTestsinAnt

SeparatingPerformanceTestsfromUnitTestsinMaven



15.1.IntroducingJUnitPerf

JUnitPerfisanextensionofJUnit3(seeSection10.1)thatadds

theabilitytotimetestcasesandtodosimpleloadand

performancetesting.Itsfeaturesaresimilartosomeofthe

TestNGannotations(seeSection11.10),butJUnitPerfusesa

verydifferentapproach,integratingsmoothlyintotheJUnit3

architecture.

UsinganelegantsystemofJUnitdecorators,JUnitPerfletsyou

useexistingunitteststobuildsimplebuteffectiveloadand

performancetests.First,youwriteyourusualunittestcasesto

verifythatthecodeisexecutingcorrectlyandisbehavingas

expected.Thenyoucanencapsulatesomeofyourkeyunittests

usingJUnitPerfdecorators,effectivelycreatingasuiteof

performancetestswithouthavingtomodifyyouroriginalunit

tests.Thisalsomakesiteasytoseparateyourperformance

testsfromyourordinaryunittests,becauseyouusuallydon't

runthematthesametime.

Incorporatingsomebasicperformancetestingintoyourunit

testsmakesgoodsense.Withoutgoingoverboard,itisagood

waytodetectmajorperformanceanomaliesearlyoninthe

piece.Youcanalsoconfiguretheseteststorunseparatelyfrom



yourordinaryunittests,inordertoavoidpenalizingthefast

feedbackcyclethatisoneofthetrademarksofgoodunittests.

Notethatwearetalkingaboutverifyingperformance,not

optimizingcodeinanuncontrolledmanner.Premature

optimizationhasoftenbeendecried,andwithsome

justification.TonyHoareisfrequentlyquotedassaying,"We

shouldforgetaboutsmallefficiencies,sayabout97percentof

thetime:prematureoptimizationistherootofallevil."

Optimizationshouldindeedbeaveryfocusedtask,withprecise

goals.Itisafutileexercisetooptimizecodethatishardlyever

used.However,makingsureyourapplicationperformscorrectly

whereitneedsto,rightfromthestart,canbeahugetimesaver.And,byformalizingtheprocessandincorporatingthe

testsintotheapplicationtestsuites,verifyingyourapplication's

performancewithJUnitPerfcanhelptoencourageamore

systematicapproachtooptimization.Ratherthanoptimizingfor

thesakeofoptimizing,youmeasuretheperformanceyouhave

andcheckitagainstwhatyouneed.Onlythendoyoudecideif

optimizationisnecessary.



15.2.MeasuringPerformancewith

TimedTests

Themostbasicperformancetestistoverifyhowlongatest

casetakestoexecute.JUnitPerfprovidesasimpleJUnit

decoratorclasscalledTimedTest,whichletsyoucheckthata

unittestdoesnottakemorethanacertaintimetorun.Inthis

section,wewilllookathowtousethisdecorator.Atthesame

time,wewillgothroughmanybasicnotionsaboutJUnitPerf.

Inthischapter,wearegoingtowritesomeperformancetests

forasimplewebapplicationthatmanagesadatabaseofmodel

planes.Onthewebapplication'shomepage,userscanconsult

thelistofknownplanetypes,selectaplanetype,andthen

viewthecorrespondingmodelplanes.Accordingtothe

performancerequirements,thishomepageisexpectedtobe

heavilyused,andneedstosupportahighload.Moreprecisely,

thespecificationsstipulatethat"Thehomepagemustbe

displayedinlessthan2seconds(notcountingnetworktraffic)

inthepresenceof10simultaneoususers."

Notethatwehavenumbershere.Performancerequirements

withoutnumbersareprettymuchuseless.Theaimof

performancetestsistoverifythatyourcodewillprovide

acceptableperformance.Ifitdoes,thereisnoneedtolook

further.Ifitdoesn't,itisbettertoknowaboutitsoonerrather

thanlater!

Asitturnsout,themainqueryinthispageistheonethat

displaysthelistofplanetypes.Intheapplication,planetypes

arerepresentedbythePlaneTypeclass.TheDAO(DataAccess

Object)classforplanetypesimplementsthefollowing

interface:

publicinterfacePlaneTypeDAO{

PlaneTypefindById(longid);

ListfindAll();

publicvoidsave(PlaneTypeplaneType);



publicvoiddelete(PlaneTypeplaneType);

}



Tolistallavailableplanetypes,weneedtoinvokethefindAll()

method.TheunittestclassforthisDAOisshownhere:

CodeView:

publicclassPlaneTypeDaoTestsextendsTestCase{

privatePlaneTypeDAOdao;

publicPlaneTypeDaoTests(Stringvalue){

super(value);

}



publicvoidsetUp()throwsSQLException{

ApplicationContextctx=SpringUtilsTestConfig.getAppli

dao=(PlaneTypeDAO)ctx.getBean("planeTypeDAO");

}



publicvoidtestFindAll(){

Listplanes=dao.findAll();

assertTrue(planes.size()>0);

...

}

...

}

























ThetestFindAll()unittestsimplyinvokesthefindAll()method,

andchecksthattheresultslistisnotempty.ThesetUp()

method,executedbeforeeachtest,obtainsaDAOobjectusing

aSpringapplicationcontext.Behindthescenes,this

instantiatestheDAO,alongwiththeappropriateJDBCdata

sourceandHibernatesession,usinganembeddedJava



database.Italsopopulatesthetestdatabasewithtestdata.

Oncewearesurethatthistestrunscorrectly,wecanmake

sureitrunsefficiently.Performanceissuescancomefrommany

sources:isonlytheminimumdataloaded,orareunnecessary

associatedobjectsloadedaswell?Isthedatabasecorrectly

indexed?

Thefirstthingtodoistocreateatestcasecontainingtheunit

testwewanttouse,asshownhere:

TestCasetestCase=newPlaneTypeDaoTests("testFindAll");



Next,wecreateaTimedTest,specifyingthistestcasealong

withthemaximumallowableexecutiontimeinmilliseconds.In

thefollowingexample,theTimedTestwaitsuntilthetestcase

finishes,andthenfailsifthefindAll()methodtookmorethan

100millisecondstoexecute:

TimedTesttimedTest=newTimedTest(testCase,100);



Ifthistestfails,theexceptionwillindicatethemaximum

allowabletimeandtheactualtimethatthemethodtooktorun:



junit.framework.AssertionFailedError:Maximumelapsedtimeexce

butwas281ms.

...

























Theadvantageofthisapproachisthatifthetestfails,you

knowbyhowmuch.Diditjustscrapeinoverthelimit,ordidit

takeanorderofmagnitudelongertofinish?Thissortof

informationcanbevitaltoknowifyouneedtoinvestigate

furtherorjustadjustyourthresholdvalues.



Bycontrast,youmayprefertoletthetestfailimmediatelyonce

thetimelimithasexpired.Thishelpstokeepthelengthofyour

testswithinreasonablelimits—ifyourteststaketoolongtorun,

youwillhaveanaturaltendencytorunthemlessoften.Todo

this,yousimplybuildthetestasfollows:

TimedTesttimedTest=newTimedTest(testCase,100,false);



Thisapproachofencapsulatingtestmethodsisknownasthe

decoratorpattern.Itisaveryflexiblewayofextendingexisting

unittests.Anditmakesitveryeasytoreuseexistingtest

cases.However,itmeansthatyoucan'tjustwriteaJUnitPerf

testcaseasyouwouldanordinarytestcase.Youneedto

implementthesuite()method,andcreateaTestSuitethat

containsyourdecoratedunittests.Testsdefinedinthismethod

canbeeasilyruninIDEssuchasEclipseandNetBeans,andcan

alsoberunautomaticallyusingMavenorAnt.Thefinaltest

caselookslikethis:

importjunit.framework.Test;

importjunit.framework.TestCase;

importjunit.framework.TestSuite;

importcom.clarkware.junitperf.TimedTest;

publicclassPlaneTypeDaoPerfTestsextendsTestCase{



publicstaticTestsuite(){

TestSuitesuite=newTestSuite();

TestCasetestCase=newPlaneTypeDaoTests("testFindAll

suite.addTest(testCase);

TimedTesttimedTest=newTimedTest(testCase,500);

suite.addTest(timedTest);

returnsuite;

}



}



There'satrickhere:weactuallyaddtheplain(nopun

intended!)testcasebeforeaddingthedecoratedone.Thisis

simplytoensurethatthetimedtestdoesnotmeasureone-off

initializationtaskssuchassettingupthetestdatabase,

initializingSpringandHibernate,andsoon.Thisisimportantto

keepinmind,asyoudon'twantinitializationtaskstopollute

yourtimingdata.Itisalsousefultoknowthatthetime

measuredinaTimedTesttestcaseincludesthetimespentin

thesetUp()andtearDown()methods.So,ifyouhaveessential

andtime-consumingcodeinthesemethodsthatyoudon'twant

tomeasure,besuretofactoritoutofyourtimerthresholds.



15.3.SimulatingLoadwithLoadTests

YoucanalsodosimpleloadtestswithJUnitPerf.Loadtestsaim

atverifyingthatanapplicationwillcopewithmultiple

simultaneoususersandstillprovideacceptableresponsetimes.

Aloadtestinvolvessimulatinganumberofsimultaneoususers

byrunningaseriesofunittestsondifferentthreads.Thisis

alsousefultocheckthatyourcodeisthread-proof,whichisan

importantaspectofwebapplicationdevelopment.

Thefollowingcodewillcreateatestwithfivesimultaneous

users:

TestCasetestCase=newPlaneTypeDaoTests("testFindAll");

LoadTestloadTest=newLoadTest(testCase,5);



Thiswillstartallfivethreadssimultaneously.Thisisnotalways

whatyouneedforproperloadtesting.Formorerealistic

testing,thetestsoneachthreadshouldbespreadoutover

time,andnotallhappenatexactlythesametime.Youcan

obtainamoreevendistributionbyprovidingaTimerobject.

Thefollowingexamplewillcreate5threads,1every100

milliseconds:

TestCasetestCase=newPlaneTypeDaoTests("testFindAll");

Timertimer=newConstantTimer(100);

LoadTestloadTest=newLoadTest(testCase,5,timer);



Alternatively,youcanusetheRandomTimertocreatethenew

threadsatrandomintervals.

Theabovecodewillstartseveralsimultaneousthreadsandrun

thetestcaseonceineachthread.Ifyouwanttodoserious

loadtesting,orevenjustmakesurethatyourapplication

performscorrectlyinamultiuserenvironment,youreallyneed

torunthetestcaseseveraltimesineachthread.Torunatest



caserepeatedly,youcanusetheRepeatedTestwrapper.Inthe

followingexample,wecreatefivethreadsrandomlydistributed

overthespaceofonesecond.Ineachthread,werunthe

findAll()testcase10times:

TestCasetestCase=newPlaneTypeDaoTests("testFindAll");

Timertimer=newRandomTimer(100,500);

RepeatedTestrepeatedTest=newRepeatedTest(testCase,10);

LoadTestloadTest=newLoadTest(testCase,5,timer);



Thissortoftestwillcheckthatyourcodeworkswellina

multithreadedenvironment.However,youmayalsowanttotest

theperformanceoftheapplicationunderpressure.Youcando

thisverynicelywithJUnitPerf.Inthefollowingexample,we

checkthat100transactions,runsimultaneouslyacross5

threads,canbeexecutedwithin25seconds.Thatwouldround

outtohalfasecondpertransactionwith10users:



publicclassPlaneTypeDaoPerfTestsextendsTestCase{



publicstaticTestsuite(){

TestSuitesuite=newTestSuite();

TestCasetestCase=newPlaneTypeDaoTests("testFindAll

suite.addTest(testCase);

Timertimer=newRandomTimer(100,1000);

RepeatedTestrepeatedTest=newRepeatedTest(testCase,

LoadTestloadTest=newLoadTest(repeatedTest,5,time

TimedTesttimedTest=newTimedTest(loadTest,25000);

suite.addTest(timedTest);

returnsuite;

}

}



Now,thesenumbersarenotpluckedoutoftheblue.Our

originalrequirementsspecifiedthattheapplicationhomepage,



whichcontainsthelistofallplanetypes,mustbedisplayedin

lessthan2seconds(notcountingnetworktraffic)95percentof

thetime,inthepresenceof10simultaneoususers.Ifyour

maindomain-layerquerytakeshalfasecond,youshouldhave

notroubledisplayingthepageinlessthantwoseconds.The

exactnumbersinthisexampledon'tmatter.Thepointisyou

needtoknowwhatsortofperformanceyouwillrequirefrom

yourapplicationbeforeyoucanstarttodosensibleloadtests,

evenataunit-testinglevel.Notethatthisisnotareasonnotto

doperformancetestingatanearlystage:itismoreofareason

tomakesureyouknowwhattheperformancerequirementsare

beforeyoustarttesting.

Thiscodewillcheckthattheaveragetransactiontimeisless

than500milliseconds.Supposethattheclientalsostipulated

thefollowing(much)moredemandingrequirement:"No

transactionmusttakeoveronesecondwith10simultaneous

users."

Thismaybehardtoguaranteefromacodingperspective,but

atleastit'seasytotest.TheJUnitPerfdecorator-based

approachisextremelyflexible,andyoucanarrangethetest

wrappersinanywaythatsuitsyou.Toguaranteethatno

transactiontakesoveronesecond,yousimplyplaceatimed

testfirst,directlyencapsulatingtheunittestcase.Thisway,the

timedtestwillbeexecutedeverytimetheunittestisrun,and

notjustattheendoftheseriesasinthepreviousexample.The

followingexamplewillrun10parallelthreadsandfailifany

transactiontakesmorethan1second:



TimedTesttimedTest=newTimedTest(testCase,1000);

RepeatedTestrepeatedTest=newRepeatedTest(timedTest

Timertimer=newRandomTimer(100,1000);

LoadTestloadTest=newLoadTest(repeatedTest,5,time

suite.addTest(loadTest);



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Chapter 15. Performance Testing with JUnitPerf

Tải bản đầy đủ ngay(0 tr)

×