Unit & integration testing
Purpose: Test AEM backend code without a running instance using wcm.io AEM Mocks, plus when to write integration tests.
Who this page is for
| Audience | Why it matters to you |
|---|---|
| Backend engineers | Non-negotiable for every model/service |
The stack
| Layer | Tool |
|---|---|
| Unit (models, services, servlets) | JUnit 5 + wcm.io AEM Mocks (io.wcm.testing.mock.aem.junit5) |
| Repository fixtures | JSON files loaded into the mock context |
| Integration (against real AEM) | it.tests module, HTTP-level — reserve for critical flows |
Model test example
@ExtendWith(AemContextExtension.class)
class PlanCardModelTest {
private final AemContext ctx = new AemContext();
@BeforeEach void setUp() {
ctx.addModelsForClasses(PlanCardModel.class);
ctx.load().json("/fixtures/plan-card.json", "/content/plan");
}
@Test void derivesCoveredCount() {
ctx.currentResource("/content/plan/goldcard");
PlanCardModel model = ctx.request().adaptTo(PlanCardModel.class);
assertEquals(2, model.getCoveredCount());
}
@Test void survivesEmptyContent() {
ctx.create().resource("/content/plan/empty",
"sling:resourceType", "phi-academy/components/plancard");
ctx.currentResource("/content/plan/empty");
assertNotNull(ctx.request().adaptTo(PlanCardModel.class));
}
}
The fixture JSON mirrors real content (copy from .infinity.json on your local instance, then trim).
What to test
| Always | Rarely |
|---|---|
| Derived getters / business logic | HTL rendering (integration concern) |
| Empty/missing content behaviour | Adobe product code |
| Servlet status codes & JSON shape | Getters that only return injected values |
| Service config permutations | — |
React translation
Same philosophy as testing hooks with a mock store: fixture state in, assertions on the adapted output, no real backend needed. If a model is hard to test, it is doing too much — split it.