It becomes a tedious task for a QA team to do manual testing after each iteration for the same functionality repeatedly. In this post, I will share a few tips and tricks to writing test cases for different testing scenarios.
Specifically, let’s look at writing automated test cases for an AEM-based project. There are different modules in AEM: the core bundle, which includes OSGI services, sling servlets, ad sling models; and ui.apps, which includes AEM components, AEM pages, templates, and HTML markups.
As a project and codebase grow, it is really important to make sure that test coverage for the code is there to maintain consistency and sanity. Writing test cases for AEM is different from writing conventional Java test cases, and this can make it difficult for a beginner to write test cases for AEM application
Adobe Solution
AEM 6.5
Installation
AEM 6.5
Installation
- jdk1.8 has to be installed in your system
- Maven has to be installed in your system
- AEM 6.5 author instance has to be running
- Make sure you’re familiar with the Java Junit test framework: http://junit.org
- Make sure you’re familiar with the Java Mockito test framework: http://site.mockito.org/
- Optional: Familiarity with the PowerMockito test framework http://powermock.github.io/
Good to know
- Sling test framework (Mock and IT): https://sling.apache.org/documentation/development/sling-testing-tools.html
- AEM Mock: http://wcm.io/testing/aem-mock/ and http://wcm.io/testing/aem-mock/apidocs/
- WCMUse classes Mocking: http://aempodcast.com/2015/testing/unit-testing-wcmuse-classes-using-mockito/#.WEmiUMMrJKc
<dependencies>
...
<!-- Testing -->
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.5.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.25.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.25.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit-addons</groupId>
<artifactId>junit-addons</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.wcm</groupId>
<artifactId>io.wcm.testing.aem-mock.junit5</artifactId>
<!-- Prefer the latest version of AEM Mock Junit5 dependency -->
<version>2.5.2</version>
<scope>test</scope>
</dependency>
...
</dependencies>
Below, you’ll see the plugins and dependencies that you need to add in pom.xml under core package i.e core/pom.xml:
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<includes>
<include>*Test.java</include>
</includes>
<skipTests>${skipTests}</skipTests>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
--------------------------------------------------------------------
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
-------------------------------------------------------------------
...
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit-addons</groupId>
<artifactId>junit-addons</artifactId>
</dependency>
<dependency>
<groupId>io.wcm</groupId>
<artifactId>io.wcm.testing.aem-mock.junit5</artifactId>
</dependency>
...
Writing test cases for the generic helper class
This is the simplest use case; your generic helper class (for example, StringUtils, DateUtils) does not use any AEM libraries. For this, you can simply use Junit to write your unit test: https://www.tutorialspoint.com/junit.
Here, you need to use a @ Test annotation at the method level; for example, below I have added a @ Test annotation for the testIsEmpty() method, checking various scenarios using the assertTrue and assertFalse function by comparing expected value and actual value.
public class ExampleTest {
@Test
public void testIsEmpty() {
assertTrue(CommonStringUtil.isEmpty(“”));
assertFalse(CommonStringUtil.isEmpty(“ “));
}
}
Now, this is where it gets a bit tricky, where you need to mock certain behaviors of the bundle and implicit objects. That’s why Sling has created a Mock version of sling objects and wcm.io has created a mock version of AEM objects. Here, you can just use aem mock: https://wcm.io/testing/aem-mock/usage.html to achieve most of your use cases (AEM mock extends Sling mock).
Below are some of the common use cases you will come across while testing your service:
1) How can I mock content my service is running against?
For this, it is recommended to use contentLoader API: http://wcm.io/testing/aem-mock/usage.html to either load existing json based resources (you can simply get it by creating a resource in CRXDE and then use something like RESOURCEPATH.infinity.json to get json for that resource) or just create a mock resource using ContentBuilder context.create().resource() or ResourceBuilder context.build().resource(): http://wcm.io/testing/aem-mock/apidocs/.
Note that if you are mocking a Page object then you have to use aem mock using aemcontext.pageManager().create()
For this, it is recommended to use contentLoader API: http://wcm.io/testing/aem-mock/usage.html to either load existing json based resources (you can simply get it by creating a resource in CRXDE and then use something like RESOURCEPATH.infinity.json to get json for that resource) or just create a mock resource using ContentBuilder context.create().resource() or ResourceBuilder context.build().resource(): http://wcm.io/testing/aem-mock/apidocs/.
Note that if you are mocking a Page object then you have to use aem mock using aemcontext.pageManager().create()
2) How can I initialize properties in the bundle?
You can use register and activate the OSGI service with properties: http://wcm.io/testing/aem-mock/usage.html#Registering_OSGi_service. Here is an example using AEM context Junit5:
You can use register and activate the OSGI service with properties: http://wcm.io/testing/aem-mock/usage.html#Registering_OSGi_service. Here is an example using AEM context Junit5:
@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
}
}
3) How can I inject other services for my service?
You can either Mock service or use a register service API for this: http://wcm.io/testing/aem-mock/usage.html#Registering_OSGi_service
Note that when you inject a service to your service using Reference then you will have to register your injected service, otherwise, your test will fail.
@ExtendWith(AemContextExtension.class)
@ExtendWith(MockitoExtension.class)
public class ExampleServiceTest {
private final AemContext context = new AemContextBuilder().build();
@Mock
BlogService blogService;
@Test
public void testSomething() {
context.load().json("/content/sample/en.json");
context.registerService(BlogService.class,blogService);
Mockito.when(context.getService(BlogService.class).calledMethod(notNull).thenReturn("expectedString")
//further testing
}
}
4) How can I test a sling model?
You can use aemContext for this: http://wcm.io/testing/aem-mock/usage.html#Sling_Models.
@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();
}
//further testing
This is very similar to how you would do test cases for Service. For request and response, you either have to mock request/response object using Mockito or use Spy or use a sling request and response mock. Since a lot of methods in filter and servlet do not return any result, make Mockito verify your friend. Here is an example using simple Mockito to test a servlet:
@ExtendWith(AemContextExtension.class)
@ExtendWith(MockitoExtension.class)
public class ExampleServletTest {
private final AemContext context = new AemContextBuilder().build();
@Mock
UserService userService;
@Spy
BlogService blogService = new BlogServiceImpl();
@InjectMocks
ExampleServletTest exampleServlet = new ExampleServletTest();
@BeforeEach
public void setUp() {
context.load().json("/content/sample/en.json");
context.registerService(UserService.class,userService);
context.registerInjectActivateService(blogService);
}
@Test()
public void doGetmethod500error() throws IOException {
Map<String, Object> parameters = new HashMap<>();
parameters.put("projectId", "1234");
Mockito.when(context.getService(UserService.class).getUser("username")
.thenReturn(new Response(errorResponseMsg, 500));
MockSlingHttpServletRequest request = new MockSlingHttpServletRequest(context.resourceResolver(),
context.bundleContext());
MockSlingHttpServletResponse response = new MockSlingHttpServletResponse();
request.setParameterMap(parameters);
exampleServlet.doGet(request, response);
String endResponseStr = response.getOutputAsString();
assertNotNull(endResponseStr);
//further testing
}
}
Test cases execution
You need to open terminal with the working directory as your AEM project and will need to run this command: mvn clean test
No comments:
Post a Comment
If you have any doubts or questions, please let us know.