Expand my Community achievements bar.

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)