principles of software constructioncharlie/courses/15-214/2016-fall/slides/11-invariants...•...

Post on 09-Jul-2020

0 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

115-214

SchoolofComputerScience

PrinciplesofSoftwareConstruction:Classinvariants,immutability,andtesting

JoshBloch CharlieGarrod

215-214

Administrivia

• Homework4aduetoday,11:59p.m.• Designreviewmeetingismandatory– Butweexpectittobereallyhelpful– Feedbackisawonderfulthing

• PSA– Youhavelessthanoneweeklefttoregistertovote!Dealine isOctober11!

315-214

KeyconceptsfromTuesday…

• Internalrepresentationsmatter– Thewrongrepresentationcanbetoxic

• Codemustbecleanandconcise– Repetitionistoxic

• Goodcodinghabitsmatter

415-214

Outline

• Classinvariantsanddefensivecopying• Immutability• Testingandcoverage• Testingforcomplexenvironments• Implementationtestingwithassertions

515-214

Classinvariants

• Criticalpropertiesofthefieldsofanobject• Establishedbytheconstructor• Maintainedbypublicmethodinvocations–Maybeinvalidatedtemporarilyduringmethodexecution

615-214

Safelanguagesandrobustprograms

• UnlikeC/C++,Javalanguagesafe– Immunetobufferoverruns,wildpointers,etc.

• Makesitpossibletowriterobust classes– Correctnessdoesn’tdependonothermodules– Eveninsafelanguage,requiresprogrammereffort

715-214

Defensiveprogramming

• Assumeclientswilltrytodestroyinvariants– Mayactuallybetrue(malicioushackers)– Morelikely:honestmistakes

• Ensureclassinvariantssurviveanyinputs– Defensivecopying–Minimizingmutability

815-214

public final class Period {private final Date start, end; // Invariant: start <= end

/*** @throws IllegalArgumentException if start > end* @throws NullPointerException if start or end is null*/

public Period(Date start, Date end) {if (start.after(end))

throw new IllegalArgumentException(start + " > " + end);this.start = start;this.end = end;

}

public Date start() { return start; }public Date end() { return end; }... // Remainder omitted

}

Thisclassisnot robust

915-214

Theproblem:Date ismutable// Attack the internals of a Period instanceDate start = new Date(); // (The current time)Date end = new Date(); // " " "Period p = new Period(start, end);end.setYear(78); // Modifies internals of p!

1015-214

Thesolution:defensivecopying// Repaired constructor - defensively copies parameterspublic Period(Date start, Date end) {

this.start = new Date(start.getTime());this.end = new Date(end.getTime());if (this.start.after(this.end))throw new IllegalArgumentException(start +“ > "+ end);

}

1115-214

Afewimportantdetails• Copiesmadebefore checkingparameters• Validitycheckperformedoncopies• Eliminateswindowofvulnerabilitybetweenparametercheckandcopy

• ThwartsmultithreadedTOCTOUattack– Time-Of-Check-To-Time-Of-U

// BROKEN - Permits multithreaded attack!public Period(Date start, Date end) {

if (start.after(end))throw new IllegalArgumentException(start + " > " + end);

// Window of vulnerabilitythis.start = new Date(start.getTime());this.end = new Date(end.getTime());

}

1215-214

Anotherimportantdetail• Usedconstructor,notclone,tomakecopies– Necessarybecause Date classisnonfinal– Attackercouldimplementmalicioussubclass• Recordsreferencetoeachextantinstance• Providesattackerwithaccesstoinstancelist

• Butwhousesclone,anyway?[EJItem11]

1315-214

Unfortunately,constructorsareonlyhalfthebattle

// Accessor attack on internals of PeriodPeriod p = new Period(new Date(), new Date());Date d = p.end();p.end.setYear(78); // Modifies internals of p!

1415-214

Thesolution:moredefensivecopying

// Repaired accessors - defensively copy fieldspublic Date start() {

return new Date(start.getTime());}public Date end() {

return new Date(end.getTime());}

NowPeriodclassisrobust!

1515-214

Summary• Don’tincorporatemutableparametersintoobject;makedefensivecopies

• Returndefensivecopiesofmutablefields…• Orreturnunmodifiable viewofmutablefields• Reallesson– useimmutablecomponents– Eliminatestheneedfordefensivecopying

1615-214

Outline

• Classinvariantsanddefensivecopying• Immutability• Testingandcoverage• Testingforcomplexenvironments• Implementationtestingwithassertions

1715-214

Immutableclasses

• Classwhoseinstancescannotbemodified• Examples:String,Integer,BigInteger• How,why,andwhentousethem

1815-214

Howtowriteanimmutableclass

• Don’tprovideanymutators• Ensurethatnomethodsmaybeoverridden• Makeallfieldsfinal• Makeallfieldsprivate• Ensuresecurityofanymutablecomponents

1915-214

public final class Complex {private final double re, im;

public Complex(double re, double im) {this.re = re;this.im = im;

}

// Getters without corresponding setterspublic double realPart() { return re; }public double imaginaryPart() { return im; }

// subtract, multiply, divide similar to addpublic Complex add(Complex c) {

return new Complex(re + c.re, im + c.im);}

Immutableclassexample

2015-214

@Override public boolean equals(Object o) {if (!(o instanceof Complex)) return false;Complex c = (Complex)o;return Double.compare(re, c.re) == 0 &&

Double.compare(im, c.im) == 0;}

@Override public int hashCode() {return 31*Double.hashCode(re) + Double.hashCode(im);

}

@Override public String toString() {return String.format("%d + %di”, re, im)";

}}

Immutableclassexample(cont.)Nothinginterestinghere

2115-214

Distinguishingcharacteristic

• Returnnewinstanceinsteadofmodifying• Functionalprogramming• Mayseemunnaturalatfirst• Manyadvantages

2215-214

Advantages

• Simplicity• InherentlyThread-Safe• Canbesharedfreely• Noneedfordefensivecopies• Excellentbuildingblocks

2315-214

Majordisadvantage

• Separateinstanceforeachdistinctvalue• Creatingtheseinstancescanbecostly

BigInteger moby = ...; // A million bits longmoby = moby.flipBit(0); // Ouch!

• Problemmagnifiedformultistepoperations–Well-designedimmutableclassesprovidecommonmultistepoperationsasprimitives

– Alternative:mutablecompanionclass• e.g.,StringBuilder forString

2415-214

Whentomakeclassesimmutable

• Always,unlessthere'sagoodreasonnotto• Alwaysmakesmall“valueclasses”immutable!– Examples:Color,PhoneNumber,Unit– Date andPoint weremistakes!– Expertsoftenuselong insteadofDate

2515-214

Whentomakeclassesmutable

• Classrepresentsentitywhosestatechanges– Real-world- BankAccount,TrafficLight– Abstract- Iterator, Matcher, Collection– Processclasses- Thread,Timer

• Ifclassmustbemutable,minimizemutability– Constructorsshouldfullyinitializeinstance– Avoidreinitializemethods

2615-214

Outline

• ClassInvariants• Immutability• Testingandcoverage• Testingforcomplexenvironments• Implementationtestingwithassertions

2715-214

Whydowetest?

2815-214

Testingdecisions

• Whotests?– Developerswhowrotethecode– QualityAssuranceTeamandTechnicalWriters– Customers

• Whentotest?– Beforeandduringdevelopment– Aftermilestones– Beforeshipping– Aftershipping

2915-214

Testdrivendevelopment

• Writetestsbeforecode• Neverwritecodewithoutafailingtest• Codeuntilthefailingtestpasses

3015-214

Whyusetestdrivendevelopment?

• Forcesyoutothinkaboutinterfacesearly• Higherproductquality– Bettercodewithfewerdefects

• Highertestsuitequality• Higherproductivity• It’sfuntowatchtestspass

3115-214

TDDinpractice

• EmpiricalstudiesonTDDshow:–Mayrequiremoreeffort–Mayimprovequalityandsavetime

• SelectiveuseofTDDisbest• AlwaysuseTDDforbugreports– Regressiontests

3215-214

Howmuchtesting?

• Yougenerallycannottestallinputs– Toomany– usuallyinfinite

• Butwhenitworks,exhaustivetestingisbest!

3315-214

Whatmakesagoodtestsuite?

• Provideshighconfidencethatcodeiscorrect• Short,clear,andnon-repetitious–Moredifficultfortestsuitesthanregularcode– Realistically,testsuiteswilllookworse

• Canbefuntowriteifapproachedinthisspirit

3415-214

Nextbestthingtoexhaustivetesting:randominputs

• Alsoknowasfuzztesting,torturetesting• Try“random”inputs,asmanyasyoucan– Chooseinputstotickleinterestingcases– Knowledgeofimplementationhelpshere

• Seedrandomnumbergeneratorsotestsrepeatable

3515-214

Black-boxtesting

• Lookatspecifications,notcode• Testrepresentativecases• Testboundaryconditions• Testinvalid(exception)cases• Don’ttestunspecifiedcases

3615-214

White-boxtesting

• Lookatspecificationsand code• Writeteststo:– Checkinterestingimplementationcases–Maximizebranchcoverage

3715-214

Codecoveragemetrics

• Methodcoverage– coarse• Branchcoverage– fine• Pathcoverage– toofine– Costishigh,valueislow– (Relatedtocyclomatic complexity)

3815-214

Coveragemetrics:usefulbutdangerous

• Cangivefalsesenseofsecurity• Examplesofwhatcoverageanalysiscouldmiss– Datavalues– Concurrencyissues– raceconditionsetc.– Usabilityproblems– Customerrequirementsissues

• Highbranchcoverageisnot sufficient

3915-214

Testsuites– idealandreal

• Idealtestsuites– Uncoverallerrorsincode– Test“non-functional”attributessuchasperformanceandsecurity

–Minimumsizeandcomplexity• RealtestSuites– Uncoversomeportionoferrorsincode– Haveerrorsoftheirown– Arenonethelesspriceless

4015-214

Outline

• Classinvariants• Immutability• Testingandcoverage• Testingforcomplexenvironments• Implementationtestingwithassertions

4115-214

Problemswhentestingsomeapps

• User-facingapplications– Usersclick,drag,etc.,andinterpretoutput– Timingissues

• Testingagainstbiginfrastructure– Databases,webservices,etc.

• Realworldeffects– Printing,mailingdocuments,etc.

• Collectivelycomprisethetestenvironment

4215-214

Example– Tiramisuapp

• Mobilerouteplanningapp• AndroidUI• BackenduseslivePATdata

4315-214

Anotherexample

• 3rdpartyFacebook apps• Androiduserinterface• BackendusesFacebookdata

4415-214

TestinginrealenvironmentsCode FacebookAndroidclient

void buttonClicked() {render(getFriends());

}List<Friend> getFriends() {

Connection c = http.getConnection();FacebookApi api = new Facebook(c);List<Node> persons = api.getFriends("john");for (Node person1 : persons) {

for (Node person2 : persons) {…}

}return result;

}

4515-214

EliminatingAndroiddependencyCode FacebookTestdriver

@Test void testGetFriends() {assert getFriends() == ...;

} List<Friend> getFriends() {

Connection c = http.getConnection();FacebookAPI api = new FacebookAPI(c);List<Node> persons = api.getFriends("john");for (Node person1 : persons) {

for (Node person2 : persons) {…}

}return result;

}

4615-214

Thatwon’tquitework

• GUIapplicationsprocessthousands ofevents• Solution:automatedGUItestingframeworks– AllowstreamsofGUIeventstobecaptured,replayed

• Thesetoolsaresometimescalledrobots

4715-214

EliminatingFacebook dependencyCode Mock

Facebook

@Test void testGetFriends() {assert getFriends() == …;

} List<Friend> getFriends() {

FacebookApi api = new MockFacebook(c);List<Node> persons = api.getFriends("john");for (Node person1 : persons) {

for (Node person2 : persons) {…}

}return result;

}

Testdriver

4815-214

Thatwon’tquitework!

• Changingproductioncodefortestingunacceptable• Problemcausedbyconstructor incode• Usefactoryinsteadofconstructor• Usetoolstofacilitatethissortoftesting– Dependencyinjectiontools,e.g.,Dagger,Guice–MockobjectframeworkssuchasMockito

4915-214

Faultinjection

• Mockscanemulatefailuressuchastimeouts• Allowsyoutoverifytherobustnessofsystem

Code MockFacebookTestdriver

5015-214

Advantagesofusingmocks

• Testcodelocallywithoutlargeenvironment• Enabledeterministictests• Enablefaultinjection• Canspeeduptestexecution– e.g.,avoidslowdatabaseaccess

• Cansimulatefunctionalitynotyetimplemented• Enabletestautomation

5115-214

DesignImplications

• Thinkabouttestabilitywhenwritingcode• Whenamockmaybeappropriate,designforit• Hidesubsystemsbehindaninterfaces• Usefactories,notconstructorstoinstantiate• Useappropriatetools– Dependencyinjectionormockingframeworks

5215-214

MoreTestingin15-313FoundationsofSoftwareEngineering• Manualtesting• Securitytesting,penetrationtesting• Fuzztestingforreliability• Usabilitytesting• GUI/Webtesting• Regressiontesting• Differentialtesting• Stress/soaktesting

5315-214

Outline

• ClassInvariants• Immutability• Testsuitesandcoverage• Testingforcomplexenvironments• Implementation-testingwithassertions

5415-21454

Whatisanassertion?

• Statementcontainingboolean expressionthatprogrammerbelievestobetrue:

assert speed <= SPEED_OF_LIGHT;

• Evaluatedatruntime– throwsError iffalse• Disabledbydefault- noperformanceeffect• Typicallyenabledduringdevelopment• Canenableinthefieldwhenproblemsoccur!

5515-214

Syntax

AssertStatement:assert Expression1 ;assert(Expression1, Expression2) ;

• Expression1 - assertedcondition(boolean)• Expression2 - detailmessageof AssertionError

55

5615-214

Whyuseassertions?

• Document&testprogrammer'sassumptions– e.g.,classinvariants

• Verifyprogrammer’sunderstanding• Quicklyuncoverbugs• Increaseconfidencethatprogramisbug-free• Assertsturnblackboxtestsintowhiteboxtests

56

5715-21457

Lookfor“assertivecomments”int remainder = i % 3;if (remainder == 0) {

...} else if (remainder == 1) {

...} else { // (remainder == 2)

...}

5815-21458

Replacewithrealassertions!int remainder = i % 3;if (remainder == 0) {

...} else if (remainder == 1) {

...} else {

assert remainder == 2;...

}

5915-21459

Usesecondargumentforfailurecapture

if (i % 3 == 0) {...

} else if (i % 3 == 1) {...

} else {assert (i % 3 == 2, i);...

}

6015-21460

Lookforswitchwithnodefaultswitch(flavor) {

case VANILLA:...break;

case CHOCOLATE:...break;

case STRAWBERRY:...

}

6115-21461

Addan“assertivedefault”switch(flavor) {case VANILLA:...break;

case CHOCOLATE:...break;

case STRAWBERRY:...break;

default:assert (false, flavor);

}

6215-21462

Donotuseassertionsforpublic preconditions

/*** Sets the refresh rate.** @param rate refresh rate, in frames per second.* @throws IllegalArgumentException if rate <= 0* or rate > MAX_REFRESH_RATE.*/public void setRefreshRate(int rate) {

if (rate <= 0 || rate > MAX_REFRESH_RATE)throw new IllegalArgumentException(...);

setRefreshInterval(1000 / rate);}

6315-21463

Do useassertionsfornon-public preconditions

/*** Sets the refresh interval (which must correspond* to a legal frame rate).** @param interval refresh interval in ms*/private void setRefreshInterval(int interval) {

assert interval > 0 && interval <= 1000, interval;... // Set the refresh interval

}

6415-21464

Do useassertionsforpostconditions/*** Returns BigInteger whose value is (this-1 mod m).* @throws ArithmeticException if m <= 0, or this* BigInteger is not relatively prime to m.*/public BigInteger modInverse(BigInteger m) {

if (m.signum() <= 0)throw new ArithmeticException(m + "<= 0");

... // Do the computationassert this.multiply(result).mod(m).equals(ONE);return result;

}

6515-21465

Complexpostconditions

void foo(int[] a) {// Manipulate contents of array...

// Array will appear unchanged}

6615-21466

Assertionsforcomplexpostconditions

void foo(final int[] a) {class DataCopy {

private int[] aCopy;DataCopy() { aCopy = (int[]) a.clone(); }boolean isConsistent() {

return Arrays.equals(a, aCopy);}

}DataCopy copy = null;assert (copy = new DataCopy()) != null;... // Manipulate contents of arrayassert copy.isConsistent();

}

6715-21467

Caveat– assertsmustnothavesideeffectsvisibleoutsideotherasserts

Dothis:boolean modified = set.remove(elt);assert modified;

Notthis:assert set.remove(elt); //Bug!

6815-21468

Sermon:acceptassertionsintoyourlife

• Programmer’sinteriormonologue:– “Nowatthispoint,weknow...”

• During,notafter,development• Quicklybecomessecondnature• Paysbigcode-qualitydividends

6915-214

Conclusion

• Tomaintainclassinvariants–Minimizemutability–Makedefensivecopieswhererequired

• Interfacetestingiscritical– Designinterfacestofacilitatetesting– Coveragetoolscanhelpgaugetestsuitequality

• Useassertionstotestimplementationdetails– Assertsamplifythevalueofyourinterfacetests

top related