AEM in 40 Days
CalendarPhase 3 · Building with the Archetype

Day 22 of 40

WKND: Unit Testing

AEM Mocks, wcm.io, JUnit 5, and what Cloud Manager measures

~50 min read0 videos1 source page

By the end of today you should be able to

  1. Write a unit test for a Sling Model using AEM Mocks
  2. Load test content from JSON into the mock repository
  3. Explain what Cloud Manager's code quality gate measures
  4. Distinguish unit tests from integration tests and know where each belongs

What you can actually unit test

Sling Models are ordinary Java, so they are testable — but they depend on a repository, a resource resolver and request context. AEM Mocks (io.wcm.testing.aem-mock) provides all of that in-memory. No running AEM, no deployment, tests measured in milliseconds.

This is the one chapter of the WKND tutorial with no Adobe video — it is written and code-only. The notes here carry the weight.

AemContext

The JUnit 5 extension AemContextExtension gives you an AemContext: a mock Sling environment with a real (in-memory) JCR, a resource resolver, a request and response, and an OSGi service registry you can populate.

@ExtendWith(AemContextExtension.class)
class BylineImplTest {

    private final AemContext ctx = new AemContext();

    @BeforeEach
    void setUp() {
        ctx.addModelsForClasses(BylineImpl.class);
        ctx.load().json("/com/adobe/aem/guides/wknd/core/models/BylineImplTest.json",
                        "/content");
    }

    @Test
    void getName_returnsAuthoredName() {
        ctx.currentResource("/content/byline");
        Byline byline = ctx.request().adaptTo(Byline.class);

        assertNotNull(byline);
        assertEquals("Jane Doe", byline.getName());
    }

    @Test
    void isEmpty_whenNoOccupations_returnsTrue() {
        ctx.currentResource("/content/empty-byline");
        Byline byline = ctx.request().adaptTo(Byline.class);

        assertTrue(byline.isEmpty());
    }
}

Test content as JSON

ctx.load().json(...) reads a JSON file from the test resources and creates those nodes in the mock repository. The format mirrors what you would get from /content/path.infinity.json on a real instance — so the quickest way to build a fixture is to author the content once, export it, and trim it down.

{
  "byline": {
    "jcr:primaryType": "nt:unstructured",
    "sling:resourceType": "wknd/components/byline",
    "name": "Jane Doe",
    "occupations": {
      "item0": { "occupation": "Writer" },
      "item1": { "occupation": "Photographer" }
    }
  },
  "empty-byline": {
    "jcr:primaryType": "nt:unstructured",
    "sling:resourceType": "wknd/components/byline"
  }
}

Mocking services

Where a model depends on an OSGi service, register a Mockito mock into the context:

ExternalService svc = mock(ExternalService.class);
when(svc.lookup("abc")).thenReturn("result");
ctx.registerService(ExternalService.class, svc);

This is also the moment poor design shows up. A model that is hard to test — because it reaches for a dozen services or does I/O in a getter — is usually telling you it is doing too much.

The Cloud Manager quality gate

Cloud Manager runs SonarQube analysis and test coverage on every production pipeline. The rules that matter in practice:

  • Line coverage has a threshold (commonly 50%) below which the gate fails.
  • Findings are tiered: Critical failures stop the pipeline; Important ones can be overridden by an authorised user with a justification; Info is advisory.
  • Security findings and use of deprecated or forbidden APIs are flagged here.
Coverage is a floor, not a goal

Chasing a percentage produces tests that assert getters return what you set. Test the logic — the branching, the null cases, the sorting, the empty state. A model with real behaviour and three meaningful tests beats one with twelve trivial ones.

Unit vs integration tests

  • Unit tests (core/src/test) — AEM Mocks, no running instance, milliseconds. The bulk of your testing.
  • Integration tests (it.tests) — run against a real AEM instance over HTTP. Slower, for things mocks cannot prove: replication, real servlet behaviour, actual rendering.
  • UI tests (ui.tests) — Cypress or Selenium against a browser. Slowest, reserved for critical author journeys.

The pyramid applies: many unit tests, some integration tests, few UI tests. Cloud Manager can run all three, and functional test failures will stop a production deployment.

Today's takeaway

AemContext plus JSON fixtures makes Sling Models genuinely unit-testable. Aim at logic rather than at a coverage number, and keep integration tests for what mocks cannot prove.

No video for this one

Adobe does not publish a video for this chapter — it is written and code-only. The notes above carry the weight today.

Read on Experience League

The primary sources these notes are drawn from.

Your notes

Saved automatically to this browser.

Check yourself

10 questions on today's material. 80% to pass.

Take the quiz