Expand my Community achievements bar.

Enhance your AEM Assets & Boost Your Development: [AEM Gems | June 19, 2024] Improving the Developer Experience with New APIs and Events

Is it Possible to test service methods with AEM Cloud?

Avatar

Level 9

We have many services which do things like call apis on external systems.

Currently, the only way we can test these end to en is to write servlets which call the services, and run the servlets via postman or similar.

 

What would be helpful is a way to:

1. use services in tests (without mocking them - using the full proper service)

2. be able to create a request to pass into services which are expecting it.

3. to have the tests only run on local builds, not on AEM builds (because AEM build servers wont have whitelisted IPs)

4. to be able to run a specific test only (via command line)

 

Which test framework is AEM using?

 

Obviously you cant just use a service in a test like you would in a servlet, e.g. this test wont work:

 

public class SomeServiceTest {

@Reference
private SomeService someService;

@Test
void someTest() throws Exception {
String result = someService.somecall(someparam);
}

}

 

We found an example like this:

  @Rule
  public final AemContext context = new AemContext();

// 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);

But we cant resolve "Rule, and we are not sure if we need all 3 of those lines, or just one of them, and what MyClass is (is it the service class, or the test class, or another class)?

 

8 Replies

Avatar

Community Advisor

Hello @TB3dock 

 

The video attached is using AemContext to test services. Please review once:

https://www.youtube.com/watch?v=BtLqsd1MDhY

 

You would have to mock at the point where you are getting the result from Third party like a Json.

For example, in our code the requests and response with an External App is done by a separate service. We mock that result.

 

when(apiEndpointServiceMock.makeApiCall(any(Map.class))).thenReturn(apiResultsJson);

  Before and after this statement, we are using AemContext for:

- all validations before making a call

- post-processing of the results.


Aanchal Sikka

Avatar

Level 9

Thanks for the reply. The crux is we dont want to mock any of the endpoints - we need to test the endpoint as an end to end test, i.e. an integration test.  we are hoping there is a way to get a handle on a service and call it.

Avatar

Community Advisor

Check out the ACS Common Junit code: https://github.com/adobe/aem-core-wcm-components/tree/39bd02462b1ac7b8bd48e3033964ad39f8038b14/testi... .

This will give you an idea of how to write Junit test cases in AEM.

Avatar

Level 9

Thanks for the link, there are some useful examples in there, but I cant find the main one - how to call a service from a test.

Avatar

Level 6

@TB3dock

This is a basic skeleton of Test class which you can use. You don't need to use @Rule annotation here.

 

import io.wcm.testing.mock.aem.junit5.AemContext;
import io.wcm.testing.mock.aem.junit5.AemContextExtension;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(AemContextExtension.class)
public class TestClass {
    public final AemContext aemContext = new AemContext();
    private ServiceUnderTest service;

    @BeforeEach
    void setup(AemContext context) {
        service = aemContext.registerInjectActivateService(new ServiceUnderTestImpl());
    }

    @Test
    void testMethod(){	
        service.serviceMethodCall();
    }
}

 

 Those three lines you have shared are different ways of registering and getting service class object in test classes. 1st and 3rd line should be used together or you can just use 3rd line. It does both register and inject service.  

Alternatively you can pass configuration as well like below, if there is any in your service.

 

Map<Object,Object> config = new HashMap<Object,Object>();
config.put("property1","value1");
config.put("property2",Boolean.FALSE);
service = aemContext.registerInjectActivateService(new ServiceUnderTestImpl(),config );

 

 Hope this helps.

Thanks

Swapnil

Avatar

Level 9

Thanks this is very helpful, but when I do this I get this error:

 

[ERROR] myTest  Time elapsed: 0.004 s  <<< ERROR!

org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [io.wcm.testing.mock.aem.junit5.AemContext arg0] in method [void com.mycompan.MyTest.setup(io.wcm.testing.mock.aem.junit5.AemContext)].

 

I think its because of the parameter to setup.  If I remove that parameter, I get a this error:

 

org.apache.sling.testing.mock.osgi.ReferenceViolationException: Unable to inject mandatory reference 'myService' for class com.bedegaming.eyas.aem.core.services.impl.MyServceImpl : no matching services were found.

 

In the root pom I see this:

 

<dependency>
<groupId>io.wcm</groupId>
<artifactId>io.wcm.testing.aem-mock.junit5</artifactId>
<version>3.0.2</version>
<scope>test</scope>
</dependency>

 

Avatar

Level 9

The crux is this: the service we want to test calls another service, and this other service does not get instantiated, so the tests fail with:

 

org.apache.sling.testing.mock.osgi.ReferenceViolationException: Unable to inject mandatory reference 'myOtherService' for class com.myCompany.impl.MyServiceImpl : no matching services were found.

 

Ive tried all 4 ways to inject MyService into the tests, but all result in MyService not being able to resolve MyOtherService.

 

CAn AEM not handle this case?