SlideShare a Scribd company logo
Unit Testing
at Scale
Jan Wloka
jan.wloka@quatico.com
Quatico Solutions AG
Unit Testing at Scale
Unit Testing at Scale
Text Image Component
3
Text Image Component
JSP Template
class
TextImageItem
class
TextImageController
<creates>
<creates>
<uses>
<uses>
3
Text Image Component
MVC
JSP Template
class
TextImageItem
class
TextImageController
<creates>
<creates>
<uses>
<uses>
3
Testing Text Image Component
AEM
Resources
App
TextImage
TextImage
Test
<creates>
<tests>
4
Test: Image Legend@Test
public void testGetImageLegendWithSourceAndCaptionReturnsBoth() throws Exception {
ResourceResolver resolver = new ResourceResolverImpl(client);
createPage(“/content/foobar”, new Properties(), PageType.PRESENCE.getTemplate());
createPage(“/content/foobar/ko”, new Properties(), PageType.LANGUAGE.getTemplate());
createPage(“/content/foobar/ko/home", new Properties(), PageType.HOME.getTemplate());
createPage(“/content/foobar/ko/home/content", new Properties(), PageType.DETAIL.getTemplate());
Resource contentPage = resolver.getResource(“/content/foobar/ko/home/content");
createResource(contentPage.getPath() + "/jcr:content/textimage", new Properties());
Resource target = resolver.getResource(contentPage.getPath() + "/jcr:content/textimage");
Properties imageProps = new Properties()
.append("source", "<sourceValue>")
.append("caption", "titleValue")
.append("sling:resourceType", ComponentType.IMAGE)
.append(JCR_LASTMODIFIED, new Time().format(DateFormat.GMT))
.append(JCR_LAST_MODIFIED_BY, "admin");
createResource(contentPage.getPath() + "/jcr:content/textimage/image", imageProps);
TextImageController testObj = new TextImageController().setup(createContext(target, contentPage));
assertEquals("titleValue<br />&lt;sourceValue&gt;", testObj.getImageLegend());
}
5
Test: Image Legend@Test
public void testGetImageLegendWithSourceAndCaptionReturnsBoth() throws Exception {
ResourceResolver resolver = new ResourceResolverImpl(client);
createPage(“/content/foobar”, new Properties(), PageType.PRESENCE.getTemplate());
createPage(“/content/foobar/ko”, new Properties(), PageType.LANGUAGE.getTemplate());
createPage(“/content/foobar/ko/home", new Properties(), PageType.HOME.getTemplate());
createPage(“/content/foobar/ko/home/content", new Properties(), PageType.DETAIL.getTemplate());
Resource contentPage = resolver.getResource(“/content/foobar/ko/home/content");
createResource(contentPage.getPath() + "/jcr:content/textimage", new Properties());
Resource target = resolver.getResource(contentPage.getPath() + "/jcr:content/textimage");
Properties imageProps = new Properties()
.append("source", "<sourceValue>")
.append("caption", "titleValue")
.append("sling:resourceType", ComponentType.IMAGE)
.append(JCR_LASTMODIFIED, new Time().format(DateFormat.GMT))
.append(JCR_LAST_MODIFIED_BY, "admin");
createResource(contentPage.getPath() + "/jcr:content/textimage/image", imageProps);
TextImageController testObj = new TextImageController().setup(createContext(target, contentPage));
assertEquals("titleValue<br />&lt;sourceValue&gt;", testObj.getImageLegend());
}
Assemble
5
Test: Image Legend@Test
public void testGetImageLegendWithSourceAndCaptionReturnsBoth() throws Exception {
ResourceResolver resolver = new ResourceResolverImpl(client);
createPage(“/content/foobar”, new Properties(), PageType.PRESENCE.getTemplate());
createPage(“/content/foobar/ko”, new Properties(), PageType.LANGUAGE.getTemplate());
createPage(“/content/foobar/ko/home", new Properties(), PageType.HOME.getTemplate());
createPage(“/content/foobar/ko/home/content", new Properties(), PageType.DETAIL.getTemplate());
Resource contentPage = resolver.getResource(“/content/foobar/ko/home/content");
createResource(contentPage.getPath() + "/jcr:content/textimage", new Properties());
Resource target = resolver.getResource(contentPage.getPath() + "/jcr:content/textimage");
Properties imageProps = new Properties()
.append("source", "<sourceValue>")
.append("caption", "titleValue")
.append("sling:resourceType", ComponentType.IMAGE)
.append(JCR_LASTMODIFIED, new Time().format(DateFormat.GMT))
.append(JCR_LAST_MODIFIED_BY, "admin");
createResource(contentPage.getPath() + "/jcr:content/textimage/image", imageProps);
TextImageController testObj = new TextImageController().setup(createContext(target, contentPage));
assertEquals("titleValue<br />&lt;sourceValue&gt;", testObj.getImageLegend());
}
Assemble
Act
5
Test: Image Legend@Test
public void testGetImageLegendWithSourceAndCaptionReturnsBoth() throws Exception {
ResourceResolver resolver = new ResourceResolverImpl(client);
createPage(“/content/foobar”, new Properties(), PageType.PRESENCE.getTemplate());
createPage(“/content/foobar/ko”, new Properties(), PageType.LANGUAGE.getTemplate());
createPage(“/content/foobar/ko/home", new Properties(), PageType.HOME.getTemplate());
createPage(“/content/foobar/ko/home/content", new Properties(), PageType.DETAIL.getTemplate());
Resource contentPage = resolver.getResource(“/content/foobar/ko/home/content");
createResource(contentPage.getPath() + "/jcr:content/textimage", new Properties());
Resource target = resolver.getResource(contentPage.getPath() + "/jcr:content/textimage");
Properties imageProps = new Properties()
.append("source", "<sourceValue>")
.append("caption", "titleValue")
.append("sling:resourceType", ComponentType.IMAGE)
.append(JCR_LASTMODIFIED, new Time().format(DateFormat.GMT))
.append(JCR_LAST_MODIFIED_BY, "admin");
createResource(contentPage.getPath() + "/jcr:content/textimage/image", imageProps);
TextImageController testObj = new TextImageController().setup(createContext(target, contentPage));
assertEquals("titleValue<br />&lt;sourceValue&gt;", testObj.getImageLegend());
}
Assemble
Act
Assert
5
AEM Sling Testing API
@Before
public void setUp() throws Exception {
URL url = new URL(serverUrl);
httpClient = new HttpClient();
httpClient.getParams().setAuthenticationPreemptive(true);
httpClient.getState().setCredentials(new AuthScope(url.getHost(), url.getPort(), ANY_REALM), getCredentials());
client = new SlingIntegrationTestClient(httpClient);
}
private void createPage(String path, Properties properties, Template template) throws Exception {
String[] pair = splitPathName(path);
HttpMethod createReq = new PostMethod(serverUrl + "/bin/wcmcommand");
createReq.setQueryString(new Properties().append("cmd", “createPage").append("title", pair[1])
.append("parentPath", pair[0]).append(TEMPLATE, template.getName()).toPairs());
if (HttpStatus.SC_OK != httpClient.executeMethod(createReq)) {
throw new HttpResponseException(createReq.getStatusCode(), createReq.getStatusText());
}
if (!properties.isEmpty()) {
HttpMethod changeReq = new PostMethod(serverUrl + path + "/" + JCR_CONTENT);
changeReq.setQueryString(properties.toPairs(true));
if (HttpStatus.SC_OK != httpClient.executeMethod(changeReq)) {
throw new HttpResponseException(changeReq.getStatusCode(), changeReq.getStatusText());
}
}
}
private String createResource(String path, Properties properties) throws IOException {
String nodeUrl = serverUrl + path;
client.createNode(nodeUrl, properties.<String>toMap());
return nodeUrl;
}
AEM Sling Testing API
@Before
public void setUp() throws Exception {
URL url = new URL(serverUrl);
httpClient = new HttpClient();
httpClient.getParams().setAuthenticationPreemptive(true);
httpClient.getState().setCredentials(new AuthScope(url.getHost(), url.getPort(), ANY_REALM), getCredentials());
client = new SlingIntegrationTestClient(httpClient);
}
private void createPage(String path, Properties properties, Template template) throws Exception {
String[] pair = splitPathName(path);
HttpMethod createReq = new PostMethod(serverUrl + "/bin/wcmcommand");
createReq.setQueryString(new Properties().append("cmd", “createPage").append("title", pair[1])
.append("parentPath", pair[0]).append(TEMPLATE, template.getName()).toPairs());
if (HttpStatus.SC_OK != httpClient.executeMethod(createReq)) {
throw new HttpResponseException(createReq.getStatusCode(), createReq.getStatusText());
}
if (!properties.isEmpty()) {
HttpMethod changeReq = new PostMethod(serverUrl + path + "/" + JCR_CONTENT);
changeReq.setQueryString(properties.toPairs(true));
if (HttpStatus.SC_OK != httpClient.executeMethod(changeReq)) {
throw new HttpResponseException(changeReq.getStatusCode(), changeReq.getStatusText());
}
}
}
private String createResource(String path, Properties properties) throws IOException {
String nodeUrl = serverUrl + path;
client.createNode(nodeUrl, properties.<String>toMap());
return nodeUrl;
}
Setup Client
AEM Sling Testing API
@Before
public void setUp() throws Exception {
URL url = new URL(serverUrl);
httpClient = new HttpClient();
httpClient.getParams().setAuthenticationPreemptive(true);
httpClient.getState().setCredentials(new AuthScope(url.getHost(), url.getPort(), ANY_REALM), getCredentials());
client = new SlingIntegrationTestClient(httpClient);
}
private void createPage(String path, Properties properties, Template template) throws Exception {
String[] pair = splitPathName(path);
HttpMethod createReq = new PostMethod(serverUrl + "/bin/wcmcommand");
createReq.setQueryString(new Properties().append("cmd", “createPage").append("title", pair[1])
.append("parentPath", pair[0]).append(TEMPLATE, template.getName()).toPairs());
if (HttpStatus.SC_OK != httpClient.executeMethod(createReq)) {
throw new HttpResponseException(createReq.getStatusCode(), createReq.getStatusText());
}
if (!properties.isEmpty()) {
HttpMethod changeReq = new PostMethod(serverUrl + path + "/" + JCR_CONTENT);
changeReq.setQueryString(properties.toPairs(true));
if (HttpStatus.SC_OK != httpClient.executeMethod(changeReq)) {
throw new HttpResponseException(changeReq.getStatusCode(), changeReq.getStatusText());
}
}
}
private String createResource(String path, Properties properties) throws IOException {
String nodeUrl = serverUrl + path;
client.createNode(nodeUrl, properties.<String>toMap());
return nodeUrl;
}
Create
Page
AEM Sling Testing API
@Before
public void setUp() throws Exception {
URL url = new URL(serverUrl);
httpClient = new HttpClient();
httpClient.getParams().setAuthenticationPreemptive(true);
httpClient.getState().setCredentials(new AuthScope(url.getHost(), url.getPort(), ANY_REALM), getCredentials());
client = new SlingIntegrationTestClient(httpClient);
}
private void createPage(String path, Properties properties, Template template) throws Exception {
String[] pair = splitPathName(path);
HttpMethod createReq = new PostMethod(serverUrl + "/bin/wcmcommand");
createReq.setQueryString(new Properties().append("cmd", “createPage").append("title", pair[1])
.append("parentPath", pair[0]).append(TEMPLATE, template.getName()).toPairs());
if (HttpStatus.SC_OK != httpClient.executeMethod(createReq)) {
throw new HttpResponseException(createReq.getStatusCode(), createReq.getStatusText());
}
if (!properties.isEmpty()) {
HttpMethod changeReq = new PostMethod(serverUrl + path + "/" + JCR_CONTENT);
changeReq.setQueryString(properties.toPairs(true));
if (HttpStatus.SC_OK != httpClient.executeMethod(changeReq)) {
throw new HttpResponseException(changeReq.getStatusCode(), changeReq.getStatusText());
}
}
}
private String createResource(String path, Properties properties) throws IOException {
String nodeUrl = serverUrl + path;
client.createNode(nodeUrl, properties.<String>toMap());
return nodeUrl;
}
Setup Resource
Why do you write tests?
7
Why do you write tests?
Feedback!
7
Why do you write tests?
Run tests as change detector? Run frequently, run fast!
Feedback!
7
Integrated tests seem great,
at first
“When using Java models, a common approach is
to use unit tests to do this, and rely on Mockito or
similar frameworks to simulate the behaviour of the
AEM environment. Within such a rich environment,
however, the test code quickly becomes hard to
follow, as most of it is setting up the AEM
simulation. Worse still, it’s very easy to get the
simulation wrong, meaning your tests pass but
your code is actually buggy.” 

— Jon Barber, In-container testing for AEM projects
8
What it’s missing?
9
What it’s missing?
• Integrated tests
✓ Can reproduce a bug
– Run slow, thus infrequently —> delay feedback
– No design feedback
9
What it’s missing?
• Integrated tests
✓ Can reproduce a bug
– Run slow, thus infrequently —> delay feedback
– No design feedback
• Isolated tests
✓ Can reproduce a bug
✓ Run fast, thus frequently —> immediate feedback
✓ Put pressure on the design —> design feedback!
9
Design 

Feedback vs. Degradation
100%
tests passed; but
bug in Code
Too many
dependencies; write
integrated tests
Slower
feedback = less time
to write tests; more
opportunities for
bugs
Harder
to write unit tests;
write more integrated
tests
No design
feedback; more
sloppy design
Design
Degradation
by J.B. Rainsberger
Object Interactions
Client
Service
Object:c Object:s
11
Object Interactions
Client
Service
Object:c Object:s
11
Object Interactions
Client
Service
Interface
Object:c Object:s
11
Object Interactions
Client
Service
Interface
Object:c Object:s
11
Object Interactions
Client
Service
Interface
Do I ask the
correct questions?
Object:c Object:s
11
Object Interactions
Client
Service
Interface
Do I ask the
correct questions?
Can I answer all
questions you’re asking?
Object:c Object:s
11
Object Interactions
Client
Service
Interface
Do I ask the
correct questions?
Can I answer all
questions you’re asking?
Do I handle all responses?
Object:c Object:s
11
Object Interactions
Client
Service
Interface
Do I ask the
correct questions?
Can I answer all
questions you’re asking?
Do I handle all responses?
Can I give you the answer
you might think I can?
Object:c Object:s
11
Testing Object Interactions
Interface
Client
Service
Do I ask the
correct questions?
Can I answer all
questions you’re asking?
Do I handle all responses?
Can I give you the answer
you might think I can?
Object:c Object:s
12
Testing Object Interactions
Interface
Client
Service
Does C ask S only
correct questions?
Can I answer all
questions you’re asking?
Do I handle all responses?
Can I give you the answer
you might think I can?
Object:c Object:s
12
Testing Object Interactions
Interface
Client
Service
Does C ask S only
correct questions?
Does S have the
impl. C depends on?
Do I handle all responses?
Can I give you the answer
you might think I can?
Object:c Object:s
12
Testing Object Interactions
Interface
Client
Service
Does C ask S only
correct questions?
Can C handle all
responses of S?
Does S have the
impl. C depends on?
Can I give you the answer
you might think I can?
Object:c Object:s
12
Testing Object Interactions
Interface
Client
Service
Does C ask S only
correct questions?
Can C handle all
responses of S?
Does S have the
impl. C depends on?
Does S’s impl. responds
in the way C expects?
Object:c Object:s
12
Testing Object Interactions
Interface
Client
Service
Does C ask S only
correct questions?
Can C handle all
responses of S?
Does S have the
impl. C depends on?
Does S’s impl. responds
in the way C expects?
Collaboration Test Contract Test
Object:c Object:s
12
Test: Image Legend revisited
# init() : void
+ validate() : Status
+ isShowTitleBelow() : boolean
+ getImageLegend() : String
TextImageController
+ validate() : Status
TextImageItem
+ validate() : Status
+ getHref() : String
+ getFullViewHref() : String
+ getHtml() : String
+ getImageLegend(String) : String
+ caption : String
+ alt : String
+ source : String
+ fullViewMaxWidth : String
+ enableFullView : Boolean
+ imageSize : ImageSize
+ imagePosition : ImagePosition
+ imageData : ImageDataItem
ImageItem
+ title : String
+ text : String
+ isRichText : boolean
Text
+ description : String
+ text : String
+ title : String
+ target : String
+ cssClass : String
+ icon : String
+ active : boolean
+ targetPageIsLinkable : boolean
+ pathRewritingDisabled : boolean
+ escapingDisabled : boolean
+ children : List
+ urlPath : String
+ urlQueryPart : String
Link
+ testValidateWithEmptyTextResourceYieldsWarning()
+ testValidateWithTextResourceYieldsOk()
+ testIsShowTitleBelowWithNoImageIsPresentReturnsFalse()
+ testIsShowTitleBelowWithImageIsPresentAndImageSizeIsSmallAndImagePositionIsLeftReturnFalse()
+ testIsShowTitleBelowWithImageIsPresentAndImageSizeIsNormalAndImagePositionIsRightReturnsFalse()
+ testIsShowTitleBelowWithImageIsPresentAndImageSizeIsSmallAndImagePositionIsRightReturnsFalse()
+ testIsShowTitleBelowWithImageIsPresentAndImageSizeIsNormalAndImagePositionIsLeftReturnsTrue()
+ testGetImageLegendReturnsEmptyWhenNoTitleAndSourceIsSet()
+ testGetImageLegendReturnsSourceWhenOnlySourceIsSet()
+ testGetImageLegendReturnsTitleWhenOnlyTitleIsSet()
+ testGetImageLegendReturnsAppendedTitleAndSource()
TextImageControllerTest
13
Collaboration Tests
• Does client ask service only correct questions?
• Does TextImageItem ask ImageItem only correct
questions?
• Can client handle all possible responses of
service?
• Can TextImageItem handle all possible
responses of ImageItem?
14
Does TextImageItem ask only
correct questions?
15
Does TextImageItem ask only
correct questions?
YES.
15
Can TextImageItem handle all
possible responses?
@Test
public void testGetImageLegendWithNullLegendReturnsEmptyString() throws Exception {
ImageItem target = mock(ImageItem.class);
when(target.getImageLegend(anyString())).thenReturn(null);
TextImageItem testObj = new TextImageItem().setImage(target);
assertEquals(StringUtils.EMPTY, testObj.getImageLegend());
}
@Test
public void testGetImageLegendWithSomeLegendReturnsLegendString() throws Exception {
ImageItem target = mock(ImageItem.class);
when(target.getImageLegend(anyString())).thenReturn("foobar");
TextImageItem testObj = new TextImageItem().setImage(target);
assertEquals("foobar", testObj.getImageLegend());
}
16
Contract Tests
• Does service have the implementation client
depends on?
• Does ImageItem#getImageLegend(String)
have the right implementation?
• Does service’s implementation responds in the
way client expects?
• Does ImageItem#getImageLegend(String)
method responds as expected?
17
Does ImageItem#getImageLegend(String)
have the right implementation?
public void testGetImageLegendWithEmptySeparatorAndDefaultValuesReturnsEmptyString() thro
assertEquals(StringUtils.EMPTY, new ImageItem().getImageLegend(""));

}

public void testGetImageLegendWithSomeSeparatorAndDefaultValuesReturnsEmptyString() throw
assertEquals(StringUtils.EMPTY, new ImageItem().getImageLegend("SEP"));

}



public void testGetImageLegendWithEmptySeparatorAndCaptionAndSourceValueReturnsCaptionAnd
ImageItem testObj = new ImageItem().setCaption("caption").setSource("source");



assertEquals("captionsource", testObj.getImageLegend(""));

}

public void testGetImageLegendWithNullSeparatorAndCaptionAndSourceValueReturnsCaptionAndS
ImageItem testObj = new ImageItem().setCaption("caption").setSource("source");



assertEquals("captionsource", testObj.getImageLegend(null));

}
18
Does ImageItem#getImageLegend(String)
method responds as expected?
@Test
public void testGetImageLegendWithSeparatorAndCaptionOnlyReturnsCaptionValue() throws Exc
ImageItem testObj = new ImageItem().setCaption("caption");
assertEquals("caption", testObj.getImageLegend("SEP"));
}
@Test
public void testGetImageLegendWithSeparatorAndSourceValueOnlyReturnSourceValue() throws E
ImageItem testObj = new ImageItem().setSource("source");
assertEquals("source", testObj.getImageLegend("SEP"));
}
@Test
public void testGetImageLegendWithSeparatorAndCaptionAndSourceValueReturnsCaptionSeparate
ImageItem testObj = new ImageItem().setCaption("caption").setSource("source");
assertEquals("captionSEPsource", testObj.getImageLegend("SEP"));
}
19
How to write contract tests?
• Test code that is mocked out in collaboration tests
• Mocked interface method <—> contract test
• Verify the mock you did is valid
• Test parameters valid on interface
• Test responses (incl. exceptions) valid on interface
• Identical for different implementations
20
How to write collaboration tests?
• Test all possible responses (incl. exceptions)
• Mocked away implementation, stub in responses
• Check for result / side effect
• Test all possible parameter values
• Check for result / verify program behavior
21
Object-oriented Design,
simplified
22
If you find a defect…
• You do have
• a missing collaboration test
• a missing contract test
• two collaboration or contract tests that don’t
agree with each other
23
How do you know its good?
• Run Fast —> Provide frequent feedback
• Small Test Setup —> Quick to implement
• Easy to maintain —> Little data dependencies
• Cover possible calls/responses —> No need to
cover all paths
• Give design feedback —> Only well designed
systems allow it
24
• Blog: “Integrated Tests Are A Scam”, J.B.
Rainsberger — http://blog.thecodewhisperer.com/
2010/10/16/integrated-tests-are-a-scam/
• Talk: “Integrated Tests Are A Scam”, J.B.
Rainsberger — https://vimeo.com/80533536
• Book: “Growing Object-Oriented Software, Guided
by Tests”, S. Freeman, N. Pryce
References
25
Thank You.
jan.wloka@quatico.com
@crashtester

More Related Content

PDF
Test-driven Development with AEM
PPT
jQuery for beginners
PPT
JavaScript
KEY
Backbone js
KEY
Why ruby
KEY
Ruby/Rails
PDF
Mastering Oracle ADF Bindings
PDF
React, Redux and es6/7
Test-driven Development with AEM
jQuery for beginners
JavaScript
Backbone js
Why ruby
Ruby/Rails
Mastering Oracle ADF Bindings
React, Redux and es6/7

What's hot (20)

PDF
Nativescript angular
PDF
#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...
PDF
Crossing platforms with JavaScript & React
PPTX
Test and profile your Windows Phone 8 App
PDF
Node.js in action
PPT
Developing application for Windows Phone 7 in TDD
PPTX
Open Source Ajax Solution @OSDC.tw 2009
PPTX
How to perform debounce in react
RTF
Easy Button
PDF
Redux for ReactJS Programmers
PDF
Graphql, REST and Apollo
PDF
The Ring programming language version 1.7 book - Part 47 of 196
PDF
Test-Driven Development of AngularJS Applications
PDF
Basic Tutorial of React for Programmers
PDF
Persisting Data on SQLite using Room
PPTX
Basics of AngularJS
PDF
Sane Async Patterns
PDF
L2 Web App Development Guest Lecture At University of Surrey 20/11/09
PDF
Overview Of Lift Framework
PDF
Overview of The Scala Based Lift Web Framework
Nativescript angular
#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...
Crossing platforms with JavaScript & React
Test and profile your Windows Phone 8 App
Node.js in action
Developing application for Windows Phone 7 in TDD
Open Source Ajax Solution @OSDC.tw 2009
How to perform debounce in react
Easy Button
Redux for ReactJS Programmers
Graphql, REST and Apollo
The Ring programming language version 1.7 book - Part 47 of 196
Test-Driven Development of AngularJS Applications
Basic Tutorial of React for Programmers
Persisting Data on SQLite using Room
Basics of AngularJS
Sane Async Patterns
L2 Web App Development Guest Lecture At University of Surrey 20/11/09
Overview Of Lift Framework
Overview of The Scala Based Lift Web Framework
Ad

Similar to Unit Testing at Scale (20)

PPTX
Windows 8 metro applications
PPT
比XML更好用的Java Annotation
PPT
Unit testing with mock libs
KEY
Integrating Wicket with Java EE 6
PDF
Javascript Frameworks for Joomla
PDF
Experience Manager 6 Developer Features - Highlights
PDF
Workshop 23: ReactJS, React & Redux testing
KEY
Summer - The HTML5 Library for Java and Scala
PDF
Web UI test automation instruments
PPTX
Тарас Олексин - Sculpt! Your! Tests!
PDF
servlets
PDF
Revolution or Evolution in Page Object
PPTX
NetBeans Plugin Development: JRebel Experience Report
PDF
Spring 3: What's New
PPTX
Apex Testing and Best Practices
PDF
Understanding backbonejs
PDF
33rd Degree 2013, Bad Tests, Good Tests
PPTX
Bare-knuckle web development
PDF
jQuery - Chapter 5 - Ajax
PPTX
Geb qa fest2017
Windows 8 metro applications
比XML更好用的Java Annotation
Unit testing with mock libs
Integrating Wicket with Java EE 6
Javascript Frameworks for Joomla
Experience Manager 6 Developer Features - Highlights
Workshop 23: ReactJS, React & Redux testing
Summer - The HTML5 Library for Java and Scala
Web UI test automation instruments
Тарас Олексин - Sculpt! Your! Tests!
servlets
Revolution or Evolution in Page Object
NetBeans Plugin Development: JRebel Experience Report
Spring 3: What's New
Apex Testing and Best Practices
Understanding backbonejs
33rd Degree 2013, Bad Tests, Good Tests
Bare-knuckle web development
jQuery - Chapter 5 - Ajax
Geb qa fest2017
Ad

Recently uploaded (20)

PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PPTX
Transform Your Business with a Software ERP System
PPTX
ManageIQ - Sprint 268 Review - Slide Deck
PPTX
ISO 45001 Occupational Health and Safety Management System
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
PDF
Understanding Forklifts - TECH EHS Solution
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PPTX
ai tools demonstartion for schools and inter college
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
PPTX
history of c programming in notes for students .pptx
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
System and Network Administration Chapter 2
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
AI in Product Development-omnex systems
Wondershare Filmora 15 Crack With Activation Key [2025
Transform Your Business with a Software ERP System
ManageIQ - Sprint 268 Review - Slide Deck
ISO 45001 Occupational Health and Safety Management System
2025 Textile ERP Trends: SAP, Odoo & Oracle
How to Choose the Right IT Partner for Your Business in Malaysia
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
Understanding Forklifts - TECH EHS Solution
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
ai tools demonstartion for schools and inter college
PTS Company Brochure 2025 (1).pdf.......
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
history of c programming in notes for students .pptx
Upgrade and Innovation Strategies for SAP ERP Customers
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
How to Migrate SBCGlobal Email to Yahoo Easily
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
System and Network Administration Chapter 2
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
AI in Product Development-omnex systems

Unit Testing at Scale

  • 1. Unit Testing at Scale Jan Wloka jan.wloka@quatico.com Quatico Solutions AG
  • 5. Text Image Component JSP Template class TextImageItem class TextImageController <creates> <creates> <uses> <uses> 3
  • 6. Text Image Component MVC JSP Template class TextImageItem class TextImageController <creates> <creates> <uses> <uses> 3
  • 7. Testing Text Image Component AEM Resources App TextImage TextImage Test <creates> <tests> 4
  • 8. Test: Image Legend@Test public void testGetImageLegendWithSourceAndCaptionReturnsBoth() throws Exception { ResourceResolver resolver = new ResourceResolverImpl(client); createPage(“/content/foobar”, new Properties(), PageType.PRESENCE.getTemplate()); createPage(“/content/foobar/ko”, new Properties(), PageType.LANGUAGE.getTemplate()); createPage(“/content/foobar/ko/home", new Properties(), PageType.HOME.getTemplate()); createPage(“/content/foobar/ko/home/content", new Properties(), PageType.DETAIL.getTemplate()); Resource contentPage = resolver.getResource(“/content/foobar/ko/home/content"); createResource(contentPage.getPath() + "/jcr:content/textimage", new Properties()); Resource target = resolver.getResource(contentPage.getPath() + "/jcr:content/textimage"); Properties imageProps = new Properties() .append("source", "<sourceValue>") .append("caption", "titleValue") .append("sling:resourceType", ComponentType.IMAGE) .append(JCR_LASTMODIFIED, new Time().format(DateFormat.GMT)) .append(JCR_LAST_MODIFIED_BY, "admin"); createResource(contentPage.getPath() + "/jcr:content/textimage/image", imageProps); TextImageController testObj = new TextImageController().setup(createContext(target, contentPage)); assertEquals("titleValue<br />&lt;sourceValue&gt;", testObj.getImageLegend()); } 5
  • 9. Test: Image Legend@Test public void testGetImageLegendWithSourceAndCaptionReturnsBoth() throws Exception { ResourceResolver resolver = new ResourceResolverImpl(client); createPage(“/content/foobar”, new Properties(), PageType.PRESENCE.getTemplate()); createPage(“/content/foobar/ko”, new Properties(), PageType.LANGUAGE.getTemplate()); createPage(“/content/foobar/ko/home", new Properties(), PageType.HOME.getTemplate()); createPage(“/content/foobar/ko/home/content", new Properties(), PageType.DETAIL.getTemplate()); Resource contentPage = resolver.getResource(“/content/foobar/ko/home/content"); createResource(contentPage.getPath() + "/jcr:content/textimage", new Properties()); Resource target = resolver.getResource(contentPage.getPath() + "/jcr:content/textimage"); Properties imageProps = new Properties() .append("source", "<sourceValue>") .append("caption", "titleValue") .append("sling:resourceType", ComponentType.IMAGE) .append(JCR_LASTMODIFIED, new Time().format(DateFormat.GMT)) .append(JCR_LAST_MODIFIED_BY, "admin"); createResource(contentPage.getPath() + "/jcr:content/textimage/image", imageProps); TextImageController testObj = new TextImageController().setup(createContext(target, contentPage)); assertEquals("titleValue<br />&lt;sourceValue&gt;", testObj.getImageLegend()); } Assemble 5
  • 10. Test: Image Legend@Test public void testGetImageLegendWithSourceAndCaptionReturnsBoth() throws Exception { ResourceResolver resolver = new ResourceResolverImpl(client); createPage(“/content/foobar”, new Properties(), PageType.PRESENCE.getTemplate()); createPage(“/content/foobar/ko”, new Properties(), PageType.LANGUAGE.getTemplate()); createPage(“/content/foobar/ko/home", new Properties(), PageType.HOME.getTemplate()); createPage(“/content/foobar/ko/home/content", new Properties(), PageType.DETAIL.getTemplate()); Resource contentPage = resolver.getResource(“/content/foobar/ko/home/content"); createResource(contentPage.getPath() + "/jcr:content/textimage", new Properties()); Resource target = resolver.getResource(contentPage.getPath() + "/jcr:content/textimage"); Properties imageProps = new Properties() .append("source", "<sourceValue>") .append("caption", "titleValue") .append("sling:resourceType", ComponentType.IMAGE) .append(JCR_LASTMODIFIED, new Time().format(DateFormat.GMT)) .append(JCR_LAST_MODIFIED_BY, "admin"); createResource(contentPage.getPath() + "/jcr:content/textimage/image", imageProps); TextImageController testObj = new TextImageController().setup(createContext(target, contentPage)); assertEquals("titleValue<br />&lt;sourceValue&gt;", testObj.getImageLegend()); } Assemble Act 5
  • 11. Test: Image Legend@Test public void testGetImageLegendWithSourceAndCaptionReturnsBoth() throws Exception { ResourceResolver resolver = new ResourceResolverImpl(client); createPage(“/content/foobar”, new Properties(), PageType.PRESENCE.getTemplate()); createPage(“/content/foobar/ko”, new Properties(), PageType.LANGUAGE.getTemplate()); createPage(“/content/foobar/ko/home", new Properties(), PageType.HOME.getTemplate()); createPage(“/content/foobar/ko/home/content", new Properties(), PageType.DETAIL.getTemplate()); Resource contentPage = resolver.getResource(“/content/foobar/ko/home/content"); createResource(contentPage.getPath() + "/jcr:content/textimage", new Properties()); Resource target = resolver.getResource(contentPage.getPath() + "/jcr:content/textimage"); Properties imageProps = new Properties() .append("source", "<sourceValue>") .append("caption", "titleValue") .append("sling:resourceType", ComponentType.IMAGE) .append(JCR_LASTMODIFIED, new Time().format(DateFormat.GMT)) .append(JCR_LAST_MODIFIED_BY, "admin"); createResource(contentPage.getPath() + "/jcr:content/textimage/image", imageProps); TextImageController testObj = new TextImageController().setup(createContext(target, contentPage)); assertEquals("titleValue<br />&lt;sourceValue&gt;", testObj.getImageLegend()); } Assemble Act Assert 5
  • 12. AEM Sling Testing API @Before public void setUp() throws Exception { URL url = new URL(serverUrl); httpClient = new HttpClient(); httpClient.getParams().setAuthenticationPreemptive(true); httpClient.getState().setCredentials(new AuthScope(url.getHost(), url.getPort(), ANY_REALM), getCredentials()); client = new SlingIntegrationTestClient(httpClient); } private void createPage(String path, Properties properties, Template template) throws Exception { String[] pair = splitPathName(path); HttpMethod createReq = new PostMethod(serverUrl + "/bin/wcmcommand"); createReq.setQueryString(new Properties().append("cmd", “createPage").append("title", pair[1]) .append("parentPath", pair[0]).append(TEMPLATE, template.getName()).toPairs()); if (HttpStatus.SC_OK != httpClient.executeMethod(createReq)) { throw new HttpResponseException(createReq.getStatusCode(), createReq.getStatusText()); } if (!properties.isEmpty()) { HttpMethod changeReq = new PostMethod(serverUrl + path + "/" + JCR_CONTENT); changeReq.setQueryString(properties.toPairs(true)); if (HttpStatus.SC_OK != httpClient.executeMethod(changeReq)) { throw new HttpResponseException(changeReq.getStatusCode(), changeReq.getStatusText()); } } } private String createResource(String path, Properties properties) throws IOException { String nodeUrl = serverUrl + path; client.createNode(nodeUrl, properties.<String>toMap()); return nodeUrl; }
  • 13. AEM Sling Testing API @Before public void setUp() throws Exception { URL url = new URL(serverUrl); httpClient = new HttpClient(); httpClient.getParams().setAuthenticationPreemptive(true); httpClient.getState().setCredentials(new AuthScope(url.getHost(), url.getPort(), ANY_REALM), getCredentials()); client = new SlingIntegrationTestClient(httpClient); } private void createPage(String path, Properties properties, Template template) throws Exception { String[] pair = splitPathName(path); HttpMethod createReq = new PostMethod(serverUrl + "/bin/wcmcommand"); createReq.setQueryString(new Properties().append("cmd", “createPage").append("title", pair[1]) .append("parentPath", pair[0]).append(TEMPLATE, template.getName()).toPairs()); if (HttpStatus.SC_OK != httpClient.executeMethod(createReq)) { throw new HttpResponseException(createReq.getStatusCode(), createReq.getStatusText()); } if (!properties.isEmpty()) { HttpMethod changeReq = new PostMethod(serverUrl + path + "/" + JCR_CONTENT); changeReq.setQueryString(properties.toPairs(true)); if (HttpStatus.SC_OK != httpClient.executeMethod(changeReq)) { throw new HttpResponseException(changeReq.getStatusCode(), changeReq.getStatusText()); } } } private String createResource(String path, Properties properties) throws IOException { String nodeUrl = serverUrl + path; client.createNode(nodeUrl, properties.<String>toMap()); return nodeUrl; } Setup Client
  • 14. AEM Sling Testing API @Before public void setUp() throws Exception { URL url = new URL(serverUrl); httpClient = new HttpClient(); httpClient.getParams().setAuthenticationPreemptive(true); httpClient.getState().setCredentials(new AuthScope(url.getHost(), url.getPort(), ANY_REALM), getCredentials()); client = new SlingIntegrationTestClient(httpClient); } private void createPage(String path, Properties properties, Template template) throws Exception { String[] pair = splitPathName(path); HttpMethod createReq = new PostMethod(serverUrl + "/bin/wcmcommand"); createReq.setQueryString(new Properties().append("cmd", “createPage").append("title", pair[1]) .append("parentPath", pair[0]).append(TEMPLATE, template.getName()).toPairs()); if (HttpStatus.SC_OK != httpClient.executeMethod(createReq)) { throw new HttpResponseException(createReq.getStatusCode(), createReq.getStatusText()); } if (!properties.isEmpty()) { HttpMethod changeReq = new PostMethod(serverUrl + path + "/" + JCR_CONTENT); changeReq.setQueryString(properties.toPairs(true)); if (HttpStatus.SC_OK != httpClient.executeMethod(changeReq)) { throw new HttpResponseException(changeReq.getStatusCode(), changeReq.getStatusText()); } } } private String createResource(String path, Properties properties) throws IOException { String nodeUrl = serverUrl + path; client.createNode(nodeUrl, properties.<String>toMap()); return nodeUrl; } Create Page
  • 15. AEM Sling Testing API @Before public void setUp() throws Exception { URL url = new URL(serverUrl); httpClient = new HttpClient(); httpClient.getParams().setAuthenticationPreemptive(true); httpClient.getState().setCredentials(new AuthScope(url.getHost(), url.getPort(), ANY_REALM), getCredentials()); client = new SlingIntegrationTestClient(httpClient); } private void createPage(String path, Properties properties, Template template) throws Exception { String[] pair = splitPathName(path); HttpMethod createReq = new PostMethod(serverUrl + "/bin/wcmcommand"); createReq.setQueryString(new Properties().append("cmd", “createPage").append("title", pair[1]) .append("parentPath", pair[0]).append(TEMPLATE, template.getName()).toPairs()); if (HttpStatus.SC_OK != httpClient.executeMethod(createReq)) { throw new HttpResponseException(createReq.getStatusCode(), createReq.getStatusText()); } if (!properties.isEmpty()) { HttpMethod changeReq = new PostMethod(serverUrl + path + "/" + JCR_CONTENT); changeReq.setQueryString(properties.toPairs(true)); if (HttpStatus.SC_OK != httpClient.executeMethod(changeReq)) { throw new HttpResponseException(changeReq.getStatusCode(), changeReq.getStatusText()); } } } private String createResource(String path, Properties properties) throws IOException { String nodeUrl = serverUrl + path; client.createNode(nodeUrl, properties.<String>toMap()); return nodeUrl; } Setup Resource
  • 16. Why do you write tests? 7
  • 17. Why do you write tests? Feedback! 7
  • 18. Why do you write tests? Run tests as change detector? Run frequently, run fast! Feedback! 7
  • 19. Integrated tests seem great, at first “When using Java models, a common approach is to use unit tests to do this, and rely on Mockito or similar frameworks to simulate the behaviour of the AEM environment. Within such a rich environment, however, the test code quickly becomes hard to follow, as most of it is setting up the AEM simulation. Worse still, it’s very easy to get the simulation wrong, meaning your tests pass but your code is actually buggy.” 
 — Jon Barber, In-container testing for AEM projects 8
  • 21. What it’s missing? • Integrated tests ✓ Can reproduce a bug – Run slow, thus infrequently —> delay feedback – No design feedback 9
  • 22. What it’s missing? • Integrated tests ✓ Can reproduce a bug – Run slow, thus infrequently —> delay feedback – No design feedback • Isolated tests ✓ Can reproduce a bug ✓ Run fast, thus frequently —> immediate feedback ✓ Put pressure on the design —> design feedback! 9
  • 23. Design 
 Feedback vs. Degradation 100% tests passed; but bug in Code Too many dependencies; write integrated tests Slower feedback = less time to write tests; more opportunities for bugs Harder to write unit tests; write more integrated tests No design feedback; more sloppy design Design Degradation by J.B. Rainsberger
  • 28. Object Interactions Client Service Interface Do I ask the correct questions? Object:c Object:s 11
  • 29. Object Interactions Client Service Interface Do I ask the correct questions? Can I answer all questions you’re asking? Object:c Object:s 11
  • 30. Object Interactions Client Service Interface Do I ask the correct questions? Can I answer all questions you’re asking? Do I handle all responses? Object:c Object:s 11
  • 31. Object Interactions Client Service Interface Do I ask the correct questions? Can I answer all questions you’re asking? Do I handle all responses? Can I give you the answer you might think I can? Object:c Object:s 11
  • 32. Testing Object Interactions Interface Client Service Do I ask the correct questions? Can I answer all questions you’re asking? Do I handle all responses? Can I give you the answer you might think I can? Object:c Object:s 12
  • 33. Testing Object Interactions Interface Client Service Does C ask S only correct questions? Can I answer all questions you’re asking? Do I handle all responses? Can I give you the answer you might think I can? Object:c Object:s 12
  • 34. Testing Object Interactions Interface Client Service Does C ask S only correct questions? Does S have the impl. C depends on? Do I handle all responses? Can I give you the answer you might think I can? Object:c Object:s 12
  • 35. Testing Object Interactions Interface Client Service Does C ask S only correct questions? Can C handle all responses of S? Does S have the impl. C depends on? Can I give you the answer you might think I can? Object:c Object:s 12
  • 36. Testing Object Interactions Interface Client Service Does C ask S only correct questions? Can C handle all responses of S? Does S have the impl. C depends on? Does S’s impl. responds in the way C expects? Object:c Object:s 12
  • 37. Testing Object Interactions Interface Client Service Does C ask S only correct questions? Can C handle all responses of S? Does S have the impl. C depends on? Does S’s impl. responds in the way C expects? Collaboration Test Contract Test Object:c Object:s 12
  • 38. Test: Image Legend revisited # init() : void + validate() : Status + isShowTitleBelow() : boolean + getImageLegend() : String TextImageController + validate() : Status TextImageItem + validate() : Status + getHref() : String + getFullViewHref() : String + getHtml() : String + getImageLegend(String) : String + caption : String + alt : String + source : String + fullViewMaxWidth : String + enableFullView : Boolean + imageSize : ImageSize + imagePosition : ImagePosition + imageData : ImageDataItem ImageItem + title : String + text : String + isRichText : boolean Text + description : String + text : String + title : String + target : String + cssClass : String + icon : String + active : boolean + targetPageIsLinkable : boolean + pathRewritingDisabled : boolean + escapingDisabled : boolean + children : List + urlPath : String + urlQueryPart : String Link + testValidateWithEmptyTextResourceYieldsWarning() + testValidateWithTextResourceYieldsOk() + testIsShowTitleBelowWithNoImageIsPresentReturnsFalse() + testIsShowTitleBelowWithImageIsPresentAndImageSizeIsSmallAndImagePositionIsLeftReturnFalse() + testIsShowTitleBelowWithImageIsPresentAndImageSizeIsNormalAndImagePositionIsRightReturnsFalse() + testIsShowTitleBelowWithImageIsPresentAndImageSizeIsSmallAndImagePositionIsRightReturnsFalse() + testIsShowTitleBelowWithImageIsPresentAndImageSizeIsNormalAndImagePositionIsLeftReturnsTrue() + testGetImageLegendReturnsEmptyWhenNoTitleAndSourceIsSet() + testGetImageLegendReturnsSourceWhenOnlySourceIsSet() + testGetImageLegendReturnsTitleWhenOnlyTitleIsSet() + testGetImageLegendReturnsAppendedTitleAndSource() TextImageControllerTest 13
  • 39. Collaboration Tests • Does client ask service only correct questions? • Does TextImageItem ask ImageItem only correct questions? • Can client handle all possible responses of service? • Can TextImageItem handle all possible responses of ImageItem? 14
  • 40. Does TextImageItem ask only correct questions? 15
  • 41. Does TextImageItem ask only correct questions? YES. 15
  • 42. Can TextImageItem handle all possible responses? @Test public void testGetImageLegendWithNullLegendReturnsEmptyString() throws Exception { ImageItem target = mock(ImageItem.class); when(target.getImageLegend(anyString())).thenReturn(null); TextImageItem testObj = new TextImageItem().setImage(target); assertEquals(StringUtils.EMPTY, testObj.getImageLegend()); } @Test public void testGetImageLegendWithSomeLegendReturnsLegendString() throws Exception { ImageItem target = mock(ImageItem.class); when(target.getImageLegend(anyString())).thenReturn("foobar"); TextImageItem testObj = new TextImageItem().setImage(target); assertEquals("foobar", testObj.getImageLegend()); } 16
  • 43. Contract Tests • Does service have the implementation client depends on? • Does ImageItem#getImageLegend(String) have the right implementation? • Does service’s implementation responds in the way client expects? • Does ImageItem#getImageLegend(String) method responds as expected? 17
  • 44. Does ImageItem#getImageLegend(String) have the right implementation? public void testGetImageLegendWithEmptySeparatorAndDefaultValuesReturnsEmptyString() thro assertEquals(StringUtils.EMPTY, new ImageItem().getImageLegend(""));
 }
 public void testGetImageLegendWithSomeSeparatorAndDefaultValuesReturnsEmptyString() throw assertEquals(StringUtils.EMPTY, new ImageItem().getImageLegend("SEP"));
 }
 
 public void testGetImageLegendWithEmptySeparatorAndCaptionAndSourceValueReturnsCaptionAnd ImageItem testObj = new ImageItem().setCaption("caption").setSource("source");
 
 assertEquals("captionsource", testObj.getImageLegend(""));
 }
 public void testGetImageLegendWithNullSeparatorAndCaptionAndSourceValueReturnsCaptionAndS ImageItem testObj = new ImageItem().setCaption("caption").setSource("source");
 
 assertEquals("captionsource", testObj.getImageLegend(null));
 } 18
  • 45. Does ImageItem#getImageLegend(String) method responds as expected? @Test public void testGetImageLegendWithSeparatorAndCaptionOnlyReturnsCaptionValue() throws Exc ImageItem testObj = new ImageItem().setCaption("caption"); assertEquals("caption", testObj.getImageLegend("SEP")); } @Test public void testGetImageLegendWithSeparatorAndSourceValueOnlyReturnSourceValue() throws E ImageItem testObj = new ImageItem().setSource("source"); assertEquals("source", testObj.getImageLegend("SEP")); } @Test public void testGetImageLegendWithSeparatorAndCaptionAndSourceValueReturnsCaptionSeparate ImageItem testObj = new ImageItem().setCaption("caption").setSource("source"); assertEquals("captionSEPsource", testObj.getImageLegend("SEP")); } 19
  • 46. How to write contract tests? • Test code that is mocked out in collaboration tests • Mocked interface method <—> contract test • Verify the mock you did is valid • Test parameters valid on interface • Test responses (incl. exceptions) valid on interface • Identical for different implementations 20
  • 47. How to write collaboration tests? • Test all possible responses (incl. exceptions) • Mocked away implementation, stub in responses • Check for result / side effect • Test all possible parameter values • Check for result / verify program behavior 21
  • 49. If you find a defect… • You do have • a missing collaboration test • a missing contract test • two collaboration or contract tests that don’t agree with each other 23
  • 50. How do you know its good? • Run Fast —> Provide frequent feedback • Small Test Setup —> Quick to implement • Easy to maintain —> Little data dependencies • Cover possible calls/responses —> No need to cover all paths • Give design feedback —> Only well designed systems allow it 24
  • 51. • Blog: “Integrated Tests Are A Scam”, J.B. Rainsberger — http://blog.thecodewhisperer.com/ 2010/10/16/integrated-tests-are-a-scam/ • Talk: “Integrated Tests Are A Scam”, J.B. Rainsberger — https://vimeo.com/80533536 • Book: “Growing Object-Oriented Software, Guided by Tests”, S. Freeman, N. Pryce References 25