AEM Unit Testing: Junit vs Mockito vs SLING Mocks vs AEM Mocks | What , How & Why?

Anmol Bhardwaj
3 min readFeb 5, 2024

--

With the introduction of AEMaaCS , and the inclusion of code coverage in the Adobe CI/CD pipeline.

Adobe Cloud Manager integrates unit test execution and code coverage reporting into its CI/CD pipeline to help encourage and promote the best practice of unit testing AEM code.

Now, you will find many articles on how-to write test cases for SLING models, Configs, Util classes etc.

So , I am not going to write about it, instead, i wanted to approach this a little differently.

I want to explain where and when to use 4 of the most common frameworks used when writing test cases.

( Junit/ Mockito /SLING Mocks /AEM Mocks )

Mocking Sling Resources and/or JCR nodes

With the presence of AEM Mocks there should not be any need to manually mock Sling Resources and JCR nodes.

It’s a lot of work to do that, especially if you compare it to load a JSON structure into an in-memory repository.

Same with ResourceResolvers and JCR sessions. So don’t mock Sling resources and JCR nodes! That’s a case for AemMocks!

Using setters in services to set references

When you want to test services, the AEM Mock framework handles injections as well, you just need to use the default constructor of your service to instantiate it, and then pass it to the context.registerInjectActivate() method. If required create the referenced services before as mocks and register them as well. AemMocks comes with ways to test OSGI services and components in a very natural way (including activations and injection of references), so please use it.

Junit vs Mockito vs SLING Mocks vs AEM Mocks

@ExtendWith({MockitoExtension.class, AemContextExtension.class})
class MyComponentTest {
private final AemContext context = new AemContext();

@Mock
private ExternalDataService externalDataService;

@BeforeEach
void setUp() {
// Set up AEM context with necessary resources and sling models
context.create().resource("/content/my-site");
context.addModelsForClasses(MyContentModel.class);
context.registerService(ExternalDataService.class, externalDataService);
}

@Test
void testMyComponentWithData() {
// Mock behavior of the external service
when(externalDataService.getData("/content/my-site")).thenReturn("Mocked Data");

// Create an instance of your component
MyComponent component = context.request().adaptTo(MyComponent.class);

// Test component logic when data is available
String result = component.renderContent();

// Verify interactions and assertions
verify(externalDataService).getData("/content/my-site");
assertEquals("Expected Result: Mocked Data", result);
}

@Test
void testMyComponentWithoutData() {
// Mock behavior of the external service when data is not available
when(externalDataService.getData("/content/my-site")).thenReturn(null);

// Create an instance of your component
MyComponent component = context.request().adaptTo(MyComponent.class);

// Test component logic when data is not available
String result = component.renderContent();

// Verify interactions and assertions
verify(externalDataService).getData("/content/my-site");
assertEquals("Fallback Result: No Data Available", result);
}
}

In this example:

- We have two test cases, each focusing on a different scenario: one when data is available from the external service and one when data is not available.

- We use JUnit 5 as the test framework.

- Mockito is employed to mock the behavior of `ExternalDataService`, an external dependency.

- Apache Sling Mocks is utilized via `AemContext` to set up an AEM-like environment with a test resource and Sling models for `MyContentModel`.

  • The AEM Mocks Test Framework (by io.wcm) is used to add Sling models for `MyContentModel` and simulate AEM behavior.

--

--

Anmol Bhardwaj

Full stack AEM Developer | Adobe Certified AEM Expert | Adobe AEM Community Advisor