![Page 1: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/1.jpg)
APACHE SLING & FRIENDS TECH MEETUP BERLIN, 26-28 SEPTEMBER 2016
Test Driven Development with AEM
Jan Wloka, Quatico Solutions Inc.
![Page 2: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/2.jpg)
From the talk “Get the Flow”
2
“Test coverage is the natural enemy of fast coding. [...] It is a challenge that will be around
forever.”
![Page 3: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/3.jpg)
Web Application with Components
3
![Page 4: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/4.jpg)
Text Image Component
4
MVCtextimage.jsp
TextImageController
TextImageItem<uses>
<creates><creates>
![Page 5: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/5.jpg)
Testing Text Image Component
5
TextImageControllerTest
App
JCR
TextImageController
Pages
Resources
Assets
<tests>
<creates><creates><creates>
![Page 6: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/6.jpg)
Unit Test: getImageLegend() returns caption?
adaptTo() 2016 6
@Test public void getImageLegenWithNoSourceButTitleReturnsCaption() { Resource page = $.aDetailPageWithParents("/content/test/ko/home/page"); Resource target = $.aTextImage(page, "/jcr:content/foobar"); $.anImage(target, "image", "caption", "expected");
TextImageController testObj = new TextImageController().setup( $.aPageContext().component(target).page(page).build() ); assertEquals("expected", testObj.getImageLegend());}
![Page 7: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/7.jpg)
▪ Creation / Builder methods tailored to your application
▪ Create Resources $.aResource(String, Object…) : Resource $.aResource(Resource, String, Object…) : Resource
▪ Create Pages $.aLanguagePageWithParents(String, Object..) : Resource $.aHomePageWithParents(String, Object..) : Resource $.aDetailPageWithParents(String, Object..) : Resource $.aPageContext() : PageContextBuilder
▪ Create Components $.anImage(Resource, String, Object…) : Resource $.aSlideShow(Resource, String, Object…) : Resource
Application specific Testing API
adaptTo() 2016 7
![Page 8: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/8.jpg)
Where is this API coming from?
8
▪ Creation Methods for Resources, custom Components and Pages
aResource(String, Object...)aResource(Resource, String, Object…)
IResources
anImage(Resource, String, Object...)aSlideshow(Resource, String, Object...)
IAppComponents
aLanguagePageWithParents(String, Object...)aHomePageWithParents(String, Object...)aDetailPageWithParents(String, Object...)
IAppPages
getImageLegendWithNoSourceAndNoCaptionReturnsEmptyString()getImageLegendWithSourceButNoCaptionReturnsSource()getImageLegendWithSourceAndCaptionReturnsCombinedString()
TextImageControllerTest
createClient() : IAemClient$ : ITestSetup
UnitTestBase
ITestSetup
![Page 9: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/9.jpg)
Implementing your creation methods
adaptTo() 2016 9
public class AppComponents extends Components implements IAppComponents { ...
public Resource aTeaserConfiguration(Resource parent, TeaserType type, Object… p){ String configPath = type != null ? type.getConfigPath() : PageTeaser.PATH_EXTENSION);
Properties props = new Properties(p).appendIfNotSet( "sling:resourceType", ComponentType.TEASER_CONFIGURATION);
return resources.aResource(parent, configPath, props.toArray()); }
... }
![Page 10: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/10.jpg)
Your Application Testing API
10
Test
ing
Libr
ary
Your
API
Test
s
aResource(String, Object...)aResource(Resource, String, Object…)
IResources
anImage(Resource, String, Object...)aSlideshow(Resource, String, Object...)
IAppComponents
anAsset(String, Object... )anAsset()anAssetRendition()
IAssets
aLanguagePageWithParents(String, Object...)aHomePageWithParents(String, Object...)aDetailPageWithParents(String, Object...)
IAppPages
renderTag(TagSupport)renderTag(TagSupport, String)
ITags
getImageLegendWithNoSourceAndNoCaptionReturnsEmptyString()getImageLegendWithSourceButNoCaptionReturnsSource()getImageLegendWithSourceAndCaptionReturnsCombinedString()
TextImageControllerTest
createClient() : IAemClient$ : ITestSetup
UnitTestBase
ITestSetup
aPage(Resource, String, Object...)aPageWithParents(String, Object...)aPageContext()
IPagesaParSys(Resource, String, Object...)aFolder(Resource, String, Object...)aFolderWithParents(String, Object...)aComponentContext()
IComponents
testHasImage()testHasDescription()testHasContent()
TeaserConfigurationTest
![Page 11: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/11.jpg)
AEM Testing Library
adaptTo() 2016 11
▪ Specifically build for tailored testing APIs
▪ Based on AEM Mocks (wcm.io/testing)
▪ Provides common API for creating
▪ Resources, Pages, Components, Assets, Tags, Services
▪ Run Unit (JCR_MOCK) and Integration Tests (JCR_OAK)
▪ Get it from https://github.com/quatico-solutions/aem-testing
![Page 12: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/12.jpg)
▪ Setup your testing API, make the ‘$’ available to your tests public class UnitTestBase { protected ITestSetup $; private IAemClient client;
@Before public void setUpTestContext() throws Exception { this.client = new AemClient(Type.Unit).startUp(); // Type.Integration for integrated tests
IResources resources = new Resources(client); IAppPages pages = new AppPages(resources, client); $ = SetupFactory.create(ITestSetup.class).getSetup(
client, resources, pages, new AppComponents(resources, client),
new Assets(client), new Tags(pages), new AppServices(client)); } @After public void tearDownTestContext() throws Exception {
this.client.shutDown(); } }
Getting Started: Create your Test Setup
adaptTo() 2016 12
![Page 13: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/13.jpg)
▪ Extend UnitTestBase, access your API at ‘$’, write more tests. public class TextImageControllerTest extends UnitTestBase { @Test public void isShowTitleBelowWithImagePresentAndSizeSmallAndPositionLeftReturnFalse() throws Exception { Resource asset = $.anAsset("/content/dam/test/foo.jpg"); Resource page = $.aDetailPageWithParents("/content/test/ko/home/page"); Resource target = $.aTextImage(page, "/jcr:content/foobar", "text", "<b>hello</b>");
$.anImage(target, "image", FILE_REFERENCE, asset.getPath(), "imageSize", SMALL, "imagePosition", LEFT_ABOVE); assertFalse(new TextImageController().setup($.aPageContext().component(target).page(page).build()).isShowTitleBelow()); }
@Test public void getImageLegendWithNoSourceButTitleReturnsCaption() throws Exception { Resource page = $.aDetailPageWithParents("/content/test/ko/home/page"); Resource target = $.aTextImage(page, "/jcr:content/foobar"); $.anImage(target, "image", "caption", "expectedValue"); TextImageController testObj = new TextImageController().setup($.aPageContext().component(target).page(page).build());
assertEquals("expectedValue", testObj.getImageLegend()); }
Getting Started: Add your unit tests
adaptTo() 2016 13
![Page 14: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/14.jpg)
Let’s implement a new component
14
▪ Write a markup template
▪ Provide a custom testing API
▪ [Write an acceptance test, see it fail]*
▪ Test-drive the component
▪ [See the acceptance test pass]*
▪ [Run the component]*
▪ Sample Code is available on Github ▪ https://github.com/quatico-solutions/aem-testing-sample
*omitted here
![Page 15: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/15.jpg)
adaptTo() 2016 15
Demo: Test-drive a new component.
![Page 16: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/16.jpg)
Development Cycle with AEM
16
Implement markup & Acceptance
test
TDD controller/
model
Check acceptance test & deploy
component
Test authoring of
new component
Implement controller/model & Creation Methods
![Page 17: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/17.jpg)
Conclusion: No excuses
17
▪ Consistent creation of AEM resources
▪ Available across all components / tests
▪ Test setups cost virtually nothing
▪ New developers quickly adapt best practices
▪ You write more simple, reliable, maintainable tests
▪ Listen to their feedback more frequently
![Page 18: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/18.jpg)
Question & Answers
18
▪ Thank you!
Jan Wloka
@crashtester
![Page 19: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/19.jpg)
19
Appendix.
![Page 20: Test Driven Development with AEM - quatico.come10619d4-9750-48fe-9989... · From the talk “Get the Flow” 2 “Test coverage is the natural enemy of fast coding. [...] It is a](https://reader031.vdocuments.net/reader031/viewer/2022022707/5be5034109d3f2d7048dbf86/html5/thumbnails/20.jpg)
@Rule public AemContext context = new AemContext(ResourceResolverType.JCR_MOCK);
@Test public void getImageLegendWithSourceAndCaptionReturnsBoth() throws Exception { context.create().page("/content/foobar", PageType.PRESENCE.getTemplate().getName()); context.create().page("/content/foobar/ko", PageType.LANGUAGE.getTemplate().getName()); context.create().page("/content/foobar/ko/home", PageType.HOME.getTemplate().getName()); Page page = context.create().page("/content/foobar/ko/home/content", PageType.DETAIL.getTemplate().getName()); Resource contentResource = page.getContentResource();
Resource target = context.create().resource(contentResource.getPath() + "/textimage", new HashMap<>());
Map<String, Object> imageProps = new HashMap<>(); imageProps.put("source", "<sourceValue>"); imageProps.put("caption", "titleValue"); imageProps.put(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY, ComponentType.IMAGE); imageProps.put(JcrConstants.JCR_LASTMODIFIED, new Time().format(DateFormat.GMT)); imageProps.put(JcrConstants.JCR_LAST_MODIFIED_BY, "admin"); context.create().resource(contentResource.getPath() + "/textimage/image", imageProps);
TextImageController testObj = new TextImageController().setup(mockPageContext(target, context.resourceResolver().resolve(page.getPath())));
assertEquals("titleValue<br /><sourceValue>", testObj.getImageLegend()); }
private PageContext mockPageContext(Resource resource, Resource page) throws Exception { … }
Unit Testing w/ AEM Mocks
adaptTo() 2016 20