APACHE SLING & FRIENDS TECH MEETUP BERLIN, 26-28 SEPTEMBER 2016 - - PowerPoint PPT Presentation

apache sling amp friends tech meetup
SMART_READER_LITE
LIVE PREVIEW

APACHE SLING & FRIENDS TECH MEETUP BERLIN, 26-28 SEPTEMBER 2016 - - PowerPoint PPT Presentation

APACHE SLING & FRIENDS TECH MEETUP BERLIN, 26-28 SEPTEMBER 2016 Test Driven Development with AEM Jan Wloka, Quatico Solutions Inc. From the talk Get the Flow Test coverage is the natural enemy of fast coding. [...] It is a


slide-1
SLIDE 1

APACHE SLING & FRIENDS TECH MEETUP BERLIN, 26-28 SEPTEMBER 2016

Test Driven Development with AEM

Jan Wloka, Quatico Solutions Inc.

slide-2
SLIDE 2

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.”

slide-3
SLIDE 3

Web Application with Components

3

slide-4
SLIDE 4

Text Image Component

4

MVC

textimage.jsp TextImage Controller TextImage Item

<uses> <creates> <creates>

slide-5
SLIDE 5

Testing Text Image Component

5

TextImage ControllerTest

App JCR

TextImageController Pages Resources Assets <tests> <creates> <creates> <creates>

slide-6
SLIDE 6

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());
 }

slide-7
SLIDE 7

▪ 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

slide-8
SLIDE 8

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

slide-9
SLIDE 9

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()); } ... }

slide-10
SLIDE 10

Your Application Testing API

10

Testing Library Your API Tests

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() IPages aParSys(Resource, String, Object...) aFolder(Resource, String, Object...) aFolderWithParents(String, Object...) aComponentContext() IComponents testHasImage() testHasDescription() testHasContent() TeaserConfigurationTest

slide-11
SLIDE 11

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

slide-12
SLIDE 12

▪ 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

slide-13
SLIDE 13

▪ 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

slide-14
SLIDE 14

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

slide-15
SLIDE 15

adaptTo() 2016 15

Demo: Test-drive a new component.

slide-16
SLIDE 16

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

slide-17
SLIDE 17

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

slide-18
SLIDE 18

Question & Answers

18

▪ Thank you!

Jan Wloka @crashtester

slide-19
SLIDE 19

19

Appendix.

slide-20
SLIDE 20

@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 />&lt;sourceValue&gt;", testObj.getImageLegend()); } private PageContext mockPageContext(Resource resource, Resource page) throws Exception { … }

Unit Testing w/ AEM Mocks

adaptTo() 2016 20