Test code needs to be as clean and as simple as production code. However, when writing tests there is the ever present temptation to not be as disciplined as you should be. As a result, test code quality gradually decays over time and becomes difficult to maintain and brittle. For example, a common problem is bloated and duplicated test fixture logic. Another problem is tests that are written at too low-level, which makes them difficult to understand and change. If you are not careful, you run the risk of your test code falling into disrepair and being ignored, which defeats the purpose of having tests.
In this talk you will learn how to make tests easier to develop and maintain by using a coding style that abstracts away the details and eliminates code duplication. We describe how to simplify test fixtures by designing domain objects with fluent interfaces, and centralizing test object creation in object mothers. You will also learn how to simplify verification logic with custom assertions. We describe how to improve web tests by writing them in terms of test utility methods, instead of calling Selenium RC directly. These utility methods form an internal domain-specific language that hides low-level details, such as mouse and button clicks.
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
About ChrisAbout ChrisGrew up in England and live in Oakland, CAp g ,Over twenty years of software development experience
Building object-oriented software since 1986Using Java since 1996Using J2EE since 1999Using J2EE since 1999
Author of POJOs in ActionSpeaker at JavaOne, SpringOne, NFJS, JavaPolis, Spring Experience, etc.Chair of the eBIG Java SIG in Oakland (www ebig org)(www.ebig.org)Run a consulting and training company that helps organizations build better software faster and deploy it on Amazon EC2Founder of Cloud Tools, an open-source project f d l i J li ti A EC2 for deploying Java applications on Amazon EC2: http://code.google.com/p/cloudtools
Chris Richardson — Improving tests with Object Mothers and DSLs Slide 3
Tests - a double-edged swordTests a double edged swordTaming test fixture logicSimplifying verification codeSimplifying verification codeWriting web testsTesting Ajax applications
Chris Richardson — Improving tests with Object Mothers and DSLs 11/10/20084
A t t h t d i l d Automates what we are doing already -Right!?Run fast unit tests instead of slower web applicationweb applicationUse TDD to incrementally solve a problem
Tests are a safety netTests are a safety netConfidently change existing codeEasier to refactor code to prevent decay
Fewer bugs that impact customers Fewer bugs that impact customers AND development
Chris Richardson — Improving tests with Object Mothers and DSLs 511/10/2008Copyright (c) 2008 Chris Richardson. All rights
Poor quality test codePoor quality test codeCommon test code smells
Ob T t 't t ll h t t t dObscure Tests – you can't tell what a test doesTest code duplication – copy and paste tests
Badly structured test setup logicComplicated logic to create test fixturesE.g. the test objects (in-memory or in DB)
Sprawling web testsp a g bWeb test framework APIs are very low-level.Easily end up with large amounts of difficult to maintain code: click(),type(),…(), yp (),Lots of duplicationLots of obscure code
Chris Richardson — Improving tests with Object Mothers and DSLs 11/10/200811
Tests - a double-edged swordTests a double edged swordTaming test fixture logicSimplifying verification codeSimplifying verification codeWriting web testsTesting Ajax applications
Chris Richardson — Improving tests with Object Mothers and DSLs 11/10/200813
Constructing individual objects can be trickycan be tricky
Best way to construct an object is to use y ja non-default constructor:
Supports objects without settersSupports immutable objectsForces you to supply all required objectsConstructor can verify object stateConstructor can verify object state
Limitations of constructors:Lots of constructor arguments ⇒ code is Lots of constructor arguments ⇒ code is difficult to readOptional arguments ⇒ multiple constructors
Chris Richardson — Improving tests with Object Mothers and DSLs 11/10/200817
Constructing objects fluentlyConstructing objects fluentlyProject project = new Project(initialStatus)
("T t P j t #1").name("Test Project #1").description("Excellent project").initiatedBy(user).requirementsContactName("Rick Jones")
i t C t tE il("j @ h ").requirementsContactEmail("[email protected]").type(ProjectType.EXTERNAL_DB).artifact(ArtifactType.ARCHITECTURE).artifact(ArtifactType.DEPLOYMENT)
Use stateful object mothersUse stateful object mothers
Test instantiates object motherTest instantiates object motherObject mother's constructor
Creates aggregates (by calling their Object Creates aggregates (by calling their Object Mothers)Stores them in (public) fieldsStores them in (public) fields
Test gets the data it needs from the object motherobject mother
Chris Richardson — Improving tests with Object Mothers and DSLs 11/10/200825
Example of a stateful motherExample of a stateful motherpublic class ProjectTests extends TestCase {
public class PTrackWorld {
private final Department itDepartment;private final User itProjectManager;private final User itBusinessAnalyst;private final Project projectInCompleteState;
private Project project;private User projectManager;private User businessAnalyst;
protected void setUp() throws Exception {PTrackWorld world = new PTrackWorld();
private final Project projectInCompleteState;
…
public PTrackWorld() {itDepartment = DepartmentMother.makeItDepartment()'
public PtrackDatabaseInitializer(HibernateTemplate
Create objects using mothersPersist them
template) {this.template = template;
}
public void afterPropertiesSet() {initializeDatabase();
}
Very easy when using ORMAvoids difficult to
}
public void initializeDatabase() {world = new PTrackWorld();StateMachine stateMachine = world.getStateMachine();template.save(stateMachine);D t t itD t t ld tITD t t()Avoids difficult to
maintain flat files: CSV, SQL, XML
Department itDepartment = world.getITDepartment();template.save(itDepartment);…
}}
Chris Richardson — Improving tests with Object Mothers and DSLs Slide 27
ChoicesChoicesSame data each time vs. random dataReferenced aggregates as parameters vs Referenced aggregates as parameters vs. create those aggregates too
Tip: use descriptive namesTip: use descriptive namesBad: makeProject1(), makeProject2(), ...Better: makeProjectIn<State>() Better: makeProjectIn<State>(), …
Tip: use Object Mothers from day 1
Chris Richardson — Improving tests with Object Mothers and DSLs 2811/10/2008
Tests - a double-edged swordTests a double edged swordTaming test fixture logicSimplifying verification codeSimplifying verification codeWriting web testsTesting Ajax applications
Chris Richardson — Improving tests with Object Mothers and DSLs 11/10/200830
O ti ti j t tHi t () t(0)Operation operation = project.getHistory().get(0);assertEquals("Excellent", operation.getComments());assertEquals(projectManager, operation.getUser());assertEquals(state0, operation.getFromStatus());assertEquals(state1, operation.getToStatus());assertFalse(operation.getTimestamp().before(startTime));assertFalse(operation getTimestamp() after(endTime));
Lots of assertions = obscure codeLikely code duplication
Chris Richardson — Improving tests with Object Mothers and DSLs 11/10/200832
Verification code calls a Test Utility Verification code calls a Test Utility Method that makes assertionsHas an Intention Revealing NameHas an Intention Revealing NameBenefits:
M k th d d blMakes the code more readableEliminates duplication
Chris Richardson — Improving tests with Object Mothers and DSLs 3311/10/2008
Literate assertions with HamcrestLiterate assertions with HamcrestHamcrest is an open-source frameworkframeworkhttp://code.google.com/p/hamcrest/Readable "literate" assertionsReadable literate assertionsRich set of composable matchersLiterate error messagesgUsed by Jmock expectations
Tests - a double-edged swordTests a double edged swordTaming test fixture logicSimplifying verification codeSimplifying verification codeWriting web testsTesting Ajax applications
Chris Richardson — Improving tests with Object Mothers and DSLs 11/10/200838
API for launching and controlling the API for launching and controlling the browserSupports multiple programming Supports multiple programming languageAPI: API:
click(), type()i F P T L d()waitForPageToLoad()
isVisible (), isPresent()
Chris Richardson — Improving tests with Object Mothers and DSLs Slide 41
Intelligently evolve the languageIntelligently evolve the language
Write/record tests using low-level APIsWrite/record tests using low level APIsUse Extract Method refactoring to create the utility methodscreate the utility methodsMove methods into
A lA common superclassTest Helper classes
Chris Richardson — Improving tests with Object Mothers and DSLs 4611/10/2008
Tests - a double-edged swordTests a double edged swordTaming test fixture logicSimplifying verification codeSimplifying verification codeWriting web testsTesting Ajax applications
Chris Richardson — Improving tests with Object Mothers and DSLs 11/10/200851
The challenge of AjaxThe challenge of AjaxAjax applications behave differentlyj pp yJavaScript executes after the page loads ⇒ less deterministic, predictable behaviorClicks don't result in page loads
Triggers an Ajax request that updates the same page
DOM nodes are often hidden rather than non-existentnon-existent
assertElementPresent() ⇒ true even when the element is not visible
Chris Richardson — Improving tests with Object Mothers and DSLs 11/10/200852
Bad approach:Bad approach:Put lots of long sleeps in your codeSlows down the tests unnecessarilySlows down the tests unnecessarily
Improved approach:Loop testing for element visibility with a Loop testing for element visibility with a short sleepUse Selenium RC "wait" featureUse Selenium-RC wait feature
Chris Richardson — Improving tests with Object Mothers and DSLs 11/10/200853
SummarySummaryMessy tests will kill your applicationy y ppAggressively refactor tests to keep them simpleDefine classes with fluent interfacesUse Object Mothers to avoid duplication of test fixture logicAggressively use Test Utility Methods:
Simplify web testsHide Ajax-related issues
Chris Richardson — Improving tests with Object Mothers and DSLs 11/10/200859