Expand my Community achievements bar.

Don’t miss the AEM Skill Exchange in SF on Nov 14—hear from industry leaders, learn best practices, and enhance your AEM strategy with practical tips.

AEM Unit Test for servlet

Avatar

Level 8

I have to write a Unit Test for a servlet that uses 

 

@Reference
private transient SearchService searchService;

When I run the doGetTest I get a NullPointerException since this searchService is null:


@Test
void doGetTest() throws IOException {

MockSlingHttpServletRequest request = context.request();
MockSlingHttpServletResponse response = context.response();
underTest.doGet(request, response);

How do I inject this SearchService in my Unit Test?

Thanks in advance.

14 Replies

Avatar

Community Advisor

Hi @anasustic 

 

Try to mock the service in your test class as mentioned here : https://taylor.callsen.me/unit-testing-aem-sling-models-and-servlets/

 

Hope this helps!

 

Thanks,

Kiran Vedantam.

Avatar

Employee Advisor

Hi @anasustic ,

You can use @Mock annotations for creating the mock instance of the service as:

@Mock
SearchService mockSearchService;

Then you can initialize your mocks using initMocks() in setUp() as:

@Before
public void setUp() throws Exception {
     MockitoAnnotations.initMocks(this);
}

And then inside test you can use :

servlet.searchService = mockSearchService;

Hope this helps, thanks!

Avatar

Level 8

Thanks @milind_bachani but I do not unterstand how to do 

servlet.searchService = mockSearchService;

My servlet does not have a method searchService? 

Avatar

Level 1
import org.mockito.internal.util.reflection.Whitebox;

Whitebox.setInternalState(underTest, "searchservice", mockedSearchservice);

Avatar

Level 8

Unfortunatelly I do not have the option to import Whitebox in maven.

Avatar

Employee Advisor

Hi @anasustic , 

That is initialization of referenced service in test servlet class. 
This is analogous to :

// any referenced service can be initialized by mock using following
servlet.<referenced_service_obj_name> = <mock_service_name>;

Let me know if you still need help, thank you.

 

Avatar

Community Advisor

Hi @anasustic,

Use @Mock (org.mockito.Mock) annotation on the Service in  Test class and to inject this to the servlet under test, use @InjectMocks (org.mockito.InjectMocks) on the servlet instantiation like the below. 

 

@Mock
private SearchService searchService

@InjectMocks
private SimpleServlet underTest = new SimpleServlet();

Then in the doGetTest() method, before calling underTest.doGet(request, response) -> provide mock implementation for the searchService using when and thenReturn methods. 

Please update this thread in case of any issues. 

 

Avatar

Level 8

I do not understand what do you mean by


mock implementation for the searchService using when and thenReturn methods. 

Please update this thread in case of any issues. 

 


This is what I have 

@ExtendWith(AemContextExtension.class)
class LocationFinderByTitleServiceTest {


private final AemContext context = ComponentTestContext.createAemContext();

@Mock
SearchService searchService;

@InjectMocks
private LocationFinderByTitleService underTest = new LocationFinderByTitleService();

@BeforeEach
void beforeEach() {
context.load().json("/component/content/location/content.json", "/content/de");


Map<String, Object> pm = new HashMap<>();
pm.put("title", "Shop");
context.request().setMethod("GET");
context.request().setParameterMap(pm);
}


@Test
void doGetTest() throws IOException {

MockSlingHttpServletRequest request = context.request();
MockSlingHttpServletResponse response = context.response();


underTest.doGet(request, response);

 I am getting a NullPointerException in the underTest.doGet while the searchService is null.

Avatar

Community Advisor

You might be using some method/functionality of SearchService in your actual Servlet class, you need to provide implementation for the same here to the test class. 

Example: If you have searchService.search() and that returns a List object say for example, then we need to write

when(searchService.search()).thenReturn(any(List.class));

 

Also, in the snippet you have shared, instantiate the AemContext like below 

private AemContext context = new AemContext(ResourceResolverType.RESOURCERESOLVER_MOCK);

or

private AemContext context = new AemContext(ResourceResolverType.JCR_MOCK);

 

Avatar

Level 8

I did as you suggested and in the beforeEach() added:

context.registerInjectActivateService(new SearchService(),  "prop1", "value1");

The SearchService() has itself two @reference annotations. 

@Reference
private ResourceResolverService resourceResolverService;

@Reference
private GeocodingService geocodingService;

and now I am getting 

org.apache.sling.testing.mock.osgi.ReferenceViolationException: Unable to inject mandatory reference 'geocodingService' for class ....SearchService : no matching services were found.

 

Can you please suggest how to properly mock these two references?

Avatar

Community Advisor

@anasustic 

Register those two Services as well to the AemContext object just before injecting the SearchService.

context.registerService(ResourceResolverService.class, <<instantiation of the service impl/mocked reference>>);
context.registerService(GeocodingService.class, <<instantiation of the service impl/mocked reference>>);
context.registerInjectActivateService(new SearchService(), "prop1", "value1");

Avatar

Community Advisor

Try this

 


...
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

private SearchService searchService;

@Test
void doGetTest() throws IOException {
searchService = mock(SearchService.class);
when(searchService.get(any()).thenReturn(null);
MockSlingHttpServletRequest request = context.request();
MockSlingHttpServletResponse response = context.response();
underTest.doGet(request, response);

JsonObject jsonResponse = gson.fromJson(response.getOutputAsString(), JsonObject.class);
assertNotNull(jsonResponse.get("searchResults"));
}

 

Avatar

Level 8

My SearchService class has two @reference annotations

 

@Reference
private ResourceResolverService resourceResolverService;

@Reference
private GeocodingService geocodingService;

and two public methods with the following signatures:

public List<LocationResult> getResults(@Nonnull GoogleMapsApiConfigurationModel configModel, @Nonnull String pagePath, @Nullable String zipCity)

 

public void cacheLocations(Resource resource)

 I am not sure how to mock that in 

when(searchService.getResults(???).thenReturn(???) 

Avatar

Level 8

I did as you suggested

 

@Test
void doGetTest() throws IOException {
searchService = mock(SearchService.class);
GoogleMapsApiConfigurationModel model = new GoogleMapsApiConfigurationModel();
String one = "one";
String two = "two";
when(searchService.getResults(model, one, two)).thenReturn(null);
MockSlingHttpServletRequest request = context.request();
MockSlingHttpServletResponse response = context.response();
underTest().doGet(request, response);

 I am getting the following error when I run the test:

 

at org.apache.sling.testing.mock.osgi.OsgiServiceUtil.injectServiceReference(OsgiServiceUtil.java:435)
at org.apache.sling.testing.mock.osgi.OsgiServiceUtil.injectServices(OsgiServiceUtil.java:418)
at org.apache.sling.testing.mock.osgi.MockOsgi.injectServices(MockOsgi.java:163)
at org.apache.sling.testing.mock.osgi.context.OsgiContextImpl.registerInjectActivateService(OsgiContextImpl.java:155)
at org.apache.sling.testing.mock.osgi.context.OsgiContextImpl.registerInjectActivateService(OsgiContextImpl.java:143)