The AemContext object provides access to mock implementations of:
- OSGi Component Context
- OSGi Bundle Context
- Sling Resource Resolver
- Sling Request
- Sling Response
- Sling Script Helper
- Registering OSGi services
- Registering adapter factories
- Accessing JSON Importer
JUnit 5: AEM Context JUnit Extension
The AEM mock context can be injected into a JUnit test using a custom JUnit extension named AemContextExtension. This extension takes care of all initialization and cleanup tasks required to make sure all unit tests can run independently (and in parallel, if required).
Example:
The AEM mock context can be injected into a JUnit test using a custom JUnit extension named AemContextExtension. This extension takes care of all initialization and cleanup tasks required to make sure all unit tests can run independently (and in parallel, if required).
Example:
@ExtendWith(AemContextExtension.class)
public class ExampleTest {
private final AemContext context = new AemContext();
@Test
public void testSomething() {
Resource resource = context.resourceResolver().getResource("/content/sample/en");
Page page = resource.adaptTo(Page.class);
// further testing
}
}
It is possible to combine such a unit test with a @ExtendWith annotation e.g. for Mockito JUnit Jupiter Extension.
It is recommended to define the AemContext field as a non-static field and use @BeforeEach and @AfterEach methods if you want to execute set up or tear down code for each test run. Since version 3.0.0 AEM Mocks also supports static AemContext fields and @BeforeAll and @AfterAll methods. However, you have to make sure you have no side-effects between the tests, as all changes in the AemContext object (e.g. content written to repository or OSGi services registered) are visible to all tests in the class.
JUnit 4: AEM Context JUnit Rule
The AEM mock context can be injected into a JUnit test using a custom JUnit rule named AemContext. This rule takes care of all initialization and cleanup tasks required to make sure all unit tests can run independently (and in parallel, if required).
Example:
It is possible to combine such a unit test with a @RunWith annotation e.g. for Mockito JUnit Runner.
You do not have to care about cleaning up the registrations - this is done automatically by the AemContext rule.
The AEM mock context can be injected into a JUnit test using a custom JUnit rule named AemContext. This rule takes care of all initialization and cleanup tasks required to make sure all unit tests can run independently (and in parallel, if required).
Example:
public class ExampleTest {
@Rule
public final AemContext context = new AemContext();
@Test
public void testSomething() {
Resource resource = context.resourceResolver().getResource("/content/sample/en");
Page page = resource.adaptTo(Page.class);
// further testing
}
}
It is possible to combine such a unit test with a @RunWith annotation e.g. for Mockito JUnit Runner.
Choosing Resource Resolver Mock Type
The AEM mock context supports different resource resolver types (provided by the Sling Mocks implementation).
The AEM mock context supports different resource resolver types (provided by the Sling Mocks implementation).
Example:
private final AemContext context = new AemContext(ResourceResolverType.RESOURCERESOLVER_MOCK);
Different resource resolver mock types are supported with pros and cons, see Resource Resolver Types for details.
It is even possible to supply multiple resource resolver types in the constructor argument - in this case, the unit test is run multiple times, once for each type. But this is only relevant if you want to develop your own unit test support components that should be compatible with all resource resolver types. Normally you pick just one type which fits best for your testing needs.
private final AemContext context = new AemContext(ResourceResolverType.RESOURCERESOLVER_MOCK);
Different resource resolver mock types are supported with pros and cons, see Resource Resolver Types for details.
It is even possible to supply multiple resource resolver types in the constructor argument - in this case, the unit test is run multiple times, once for each type. But this is only relevant if you want to develop your own unit test support components that should be compatible with all resource resolver types. Normally you pick just one type which fits best for your testing needs.
Getting and Manipulating Pages
Simulating Sling Request
Example for preparing a sling request with custom request data:
// prepare sling request
Registering OSGi service
Example for registering and getting an OSGi service for a unit test:
Adapter Factories
You can register your own or existing adapter factories to support adaptions e.g. for classes extending SlingAdaptable.
Example:
@ExtendWith(AemContextExtension.class)
public class ExampleTest {
private final AemContext context = new AemContext();
@Test
public void testSomething() {
Page page = context.pageManager().getPage("/content/sample/en");
Template template = page.getTemplate();
Iterator<Page> childPages = page.listChildren();
// further testing
}
@Test
public void testPageManagerOperations() throws WCMException {
Page page = context.pageManager().create("/content/sample/en", "test1",
"/apps/sample/templates/homepage", "title1");
// further testing
context.pageManager().delete(page, false);
}
}
Simulating Sling Request
Example for preparing a sling request with custom request data:
// prepare sling request
context.request().setQueryString("param1=aaa¶m2=bbb");
context.requestPathInfo().setSelectorString("selector1.selector2");
context.requestPathInfo().setExtension("html");
// set current page
context.currentPage("/content/sample/en");
// set WCM Mode
WCMMode.EDIT.toRequest(context.request());
Example for registering and getting an OSGi service for a unit test:
// register OSGi service
context.registerService(MyClass.class, myService);
// or alternatively: inject dependencies, activate and register OSGi service
context.registerInjectActivateService(myService);
// get OSGi service
MyClass service = context.getService(MyClass.class);
// or alternatively: get OSGi service via bundle context
ServiceReference ref = context.bundleContext().getServiceReference(MyClass.class.getName());
MyClass service2 = context.bundleContext().getService(ref);
You can register your own or existing adapter factories to support adaptions e.g. for classes extending SlingAdaptable.
Example:
// register adapter factory
context.registerService(myAdapterFactory);
// test adaption
MyClass object = resource.adaptTo(MyClass.class);
You do not have to care about cleaning up the registrations - this is done automatically by the AemContext rule.
Sling Models
Example:
Note: If your model is not adaptable from SlingHttpServletRequest.class (e.g. only from Resource.class) and you rely on the extra features provided by wcm.io Sling Commons and wcm.io Sling Models which can inject request-derived objects via a ThreadLocal it might be necessary to set the request context manually to ensure injection of request-derived objects works in your unit tests.
Example:
@Before
public void setUp() {
// register models from package
context.addModelsForPackage("com.app1.models");
}
@Test
public void testSomething() {
RequestAttributeModel model = context.request().adaptTo(RequestAttributeModel.class);
// further testing
}
@Model(adaptables = SlingHttpServletRequest.class)
interface RequestAttributeModel {
@Inject
String getProp1();
}
Note: If your model is not adaptable from SlingHttpServletRequest.class (e.g. only from Resource.class) and you rely on the extra features provided by wcm.io Sling Commons and wcm.io Sling Models which can inject request-derived objects via a ThreadLocal it might be necessary to set the request context manually to ensure injection of request-derived objects works in your unit tests.
Example:
MockSlingExtensions.setRequestContext(context, context.request());
Content Policies
AEM Mock does not implement the full stack with editable templates, policy mappings, and policies stored in the repository. But it provides a shortcut way to quickly provide a content policy with some properties for any resource type and ensures these properties can be read either via Style objects or via the Content Policy API.
Example for setting a content policy for a resource type:
Setting run modes
Example:
This sets the current run mode(s) in a mock version of the SlingSettingsService.
In the unit test you can use this customized AEM context:
Context Plugins
AEM Mocks supports “Context Plugins” that hook into the lifecycle of each test run and can prepare test setup before or after the other setUp actions, and execute test teardown code before or after the other tearDown action.
To define a plugin implement the org.apache.sling.testing.mock.osgi.context.ContextPlugin<AemContextImpl> interface. For convenience it is recommended to extend the abstract class org.apache.sling.testing.mock.osgi.context.AbstractContextPlugin<AemContextImpl>. In most cases you would just override the afterSetUp method. In this method you can register additional OSGi services or do other preparation work. It is recommended to define a constant pointing to a singleton of a plugin instance for using it.
To use a plugin in your unit test class, use the AemContextBuilder class instead of directly instantiating the AemContextclass. This allows you in a fluent style to configure more options, with the plugin(...) method you can add one or more plugins.
Example:
AemContext context = new AemContextBuilder().plugin(MY_PLUGIN).build();
More examples:
wcm.io Sling Extensions Mock Helper
wcm.io Sling Extensions Mock Helper Test
MockSlingExtensions.setRequestContext(context, context.request());
Content Policies
AEM Mock does not implement the full stack with editable templates, policy mappings, and policies stored in the repository. But it provides a shortcut way to quickly provide a content policy with some properties for any resource type and ensures these properties can be read either via Style objects or via the Content Policy API.
Example for setting a content policy for a resource type:
// create a content policy with mapping for resource type
context.contentPolicyMapping("app1/componenty/component1",
"prop1", "value1",
"prop2", 123);
Example:
// set runmode for unit test
context.runMode("author");
This sets the current run mode(s) in a mock version of the SlingSettingsService.
Application-specific AEM context
When building unit test suites for your AEM application you have usually to execute always some application-specific preparation tasks, e.g. register custom services, adapter factories, or import sample content. This can be done in a convenience class using a SetupCallback. Example:
When building unit test suites for your AEM application you have usually to execute always some application-specific preparation tasks, e.g. register custom services, adapter factories, or import sample content. This can be done in a convenience class using a SetupCallback. Example:
public final class AppAemContext {
public static AemContext newAemContext() {
return new AemContext(new SetUpCallback());
}
private static final class SetUpCallback implements AemContextCallback {
@Override
public void execute(AemContext context) throws PersistenceException, IOException {
// application-specific services for unit tests
context.registerService(AdapterFactory.class, new AppAdapterFactory());
context.registerService(new AemObjectInjector());
// import sample content
context.contentLoader().json("/sample-content.json", "/content/sample/en");
// set default current page
context.currentPage("/content/sample/en");
}
}
}
In the unit test you can use this customized AEM context:
@ExtendWith(AemContextExtension.class)
public class MyTest {
private final AemContext context = AppAemContext.newAemContext();
@Test
public void testSomething() {
// do test
}
}
Context Plugins
AEM Mocks supports “Context Plugins” that hook into the lifecycle of each test run and can prepare test setup before or after the other setUp actions, and execute test teardown code before or after the other tearDown action.
To define a plugin implement the org.apache.sling.testing.mock.osgi.context.ContextPlugin<AemContextImpl> interface. For convenience it is recommended to extend the abstract class org.apache.sling.testing.mock.osgi.context.AbstractContextPlugin<AemContextImpl>. In most cases you would just override the afterSetUp method. In this method you can register additional OSGi services or do other preparation work. It is recommended to define a constant pointing to a singleton of a plugin instance for using it.
To use a plugin in your unit test class, use the AemContextBuilder class instead of directly instantiating the AemContextclass. This allows you in a fluent style to configure more options, with the plugin(...) method you can add one or more plugins.
Example:
AemContext context = new AemContextBuilder().plugin(MY_PLUGIN).build();
More examples:
wcm.io Sling Extensions Mock Helper
wcm.io Sling Extensions Mock Helper Test
No comments:
Post a Comment
If you have any doubts or questions, please let us know.