Expand my Community achievements bar.

SOLVED

Unable to inject mandatory reference in aem unit testing.

Avatar

Level 2

Hi,

I followed this AEM tutorial https://experienceleague.adobe.com/docs/experience-manager-learn/cloud-service/developing/advanced/s...

to set up a service class that loads once service user object is available. 

 

 

 

 

@Component(
        reference = {
                @Reference(
                        name = wknd-examples-statistics,
                        service = ServiceUserMapped.class,
                        target = "(subServiceName=wknd-examples-statistics)"
                )
        }
)
public class ContentStatisticsImpl implements ContentStatistics {

 

 

 

 

My service works fine. The issue is when I am unit testing.

I get this error while unit testing:

org.apache.sling.testing.mock.osgi.ReferenceViolationException: Unable to inject mandatory reference 'wknd-examples-statistics' (org.apache.sling.serviceusermapping.ServiceUserMapped) for class com.adobe.aem.wknd.examples.core.statistics.impl.ContentStatisticsImpl

: no matching services were found. bundleContext=org.apache.sling.testing.mock.osgi.MockBundleContext@298f7b0a.

 

Error occurs when I inject the service as below:

context.registerInjectActivateService(newContentStatisticsImpl());

How do i mock @reference. Please help

1 Accepted Solution

Avatar

Correct answer by
Level 2

Hi, I resolved it by adding a bind method:

@Component(
        reference = {
                @Reference(
                        name = wknd-examples-statistics,
                        service = ServiceUserMapped.class,
                        target = "(subServiceName=wknd-examples-statistics)",
                        bind = "setServiceUserMapped"
                )
        }
)
public class ContentStatisticsImpl implements ContentStatistics {
   @Reference
    private ServiceUserMapped serviceUserMapped;

    public void setServiceUserMapped(ServiceUserMapped serviceUserMapped) {
        this.serviceUserMapped = serviceUserMapped;
    }
//other code
}

Test case:
public class ContentStatisticsImplTest {
       @BeforeEach
        public void setup() {
        serviceUserMapped = mock(ServiceUserMapped.class);
        context.registerService(ServiceUserMapped.class,serviceUserMapped, ImmutableMap.of(ServiceUserMapped.SUBSERVICENAME,"wknd-examples-statistics"));
}

Thanks for your help. @sherinregi 

View solution in original post

6 Replies

Avatar

Level 8

Hi @sindhusr 

One thing i notice here is you are trying yo use registerInjectActivateService on the same class you are on. In your case you are referencing a markerservice serviceusermapped and that reference needs to be mocked inorder to prevent this exception .

 

Can you try to do inject that and use the same in setup and then invoke

 

Try something similar 

sherinregi_0-1704780939754.png

 

https://programtalk.com/java-more-examples/io.wcm.testing.mock.aem.junit.AemContext.registerService(...

Ref: https://developer.adobe.com/experience-manager/reference-materials/6-5/javadoc/org/apache/sling/serv...

 

Thanks,

Sherin

 

Avatar

Level 2

Hi @sherinregi , Thank you for your response.

What is ServiceUserMapped.clreplaced? I cannot resolve that on my IDE nor can I find references to it.

This is what I tried:

ServiceUserMapped serviceUserMapped = Mockito.mock(ServiceUserMapped.class);
context.registerService(ServiceUserMapped.class,serviceUserMapped,ImmutableMap.of(ServiceUserMapped.SUBSERVICENAME,"wknd-examples-statistics"));
context.registerInjectActivateService(newContentStatisticsImpl());

 Now I get the below error:

java.lang.RuntimeException: No bind/unbind method name or file name defined for reference wknd-examples-statistics

 

Avatar

Level 8

Hi @sindhusr 

You can ignore the clreplaced one. The steps you followed looks rite to me . The error now you are getting looks a different one and i feel it could be related to the details given in the below links. Could you please check on that 

https://groups.google.com/g/wcm-io-dev/c/JzFnXsY6XZk?pli=1

 

https://experienceleaguecommunities.adobe.com/t5/adobe-experience-manager/runtime-error-while-runnin...

 

They have explained a sequence of steps to follow. Also check the import packages for mock 

ref: https://programtalk.com/vs4/java/adobe/commerce-cif-connector/bundles/cif-virtual-catalog/src/test/j...

 

 

 

Avatar

Correct answer by
Level 2

Hi, I resolved it by adding a bind method:

@Component(
        reference = {
                @Reference(
                        name = wknd-examples-statistics,
                        service = ServiceUserMapped.class,
                        target = "(subServiceName=wknd-examples-statistics)",
                        bind = "setServiceUserMapped"
                )
        }
)
public class ContentStatisticsImpl implements ContentStatistics {
   @Reference
    private ServiceUserMapped serviceUserMapped;

    public void setServiceUserMapped(ServiceUserMapped serviceUserMapped) {
        this.serviceUserMapped = serviceUserMapped;
    }
//other code
}

Test case:
public class ContentStatisticsImplTest {
       @BeforeEach
        public void setup() {
        serviceUserMapped = mock(ServiceUserMapped.class);
        context.registerService(ServiceUserMapped.class,serviceUserMapped, ImmutableMap.of(ServiceUserMapped.SUBSERVICENAME,"wknd-examples-statistics"));
}

Thanks for your help. @sherinregi 

Avatar

Community Advisor

Hi @sindhusr 
when unit testing OSGi components that have@Reference annotations, you need to simulate the OSGi environment and register the services your component depends on. The error you're encountering indicates that the required service (wknd-examples-statistics) is not being provided in the test environment.
You can use the @Designate annotation along with @OsgiService to mock and register the required service during unit testing. Here's an example:

import org.apache.sling.serviceusermapping.ServiceUserMapped;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

@Component(
        immediate = true,
        service = ContentStatistics.class
)
@Designate(ocd = ContentStatisticsImpl.Config.class)
public class ContentStatisticsImpl implements ContentStatistics {

    @Reference
    private ServiceUserMapped wkndExamplesStatisticsService;

    // Other code...

    @Activate
    @Modified
    protected void activate(Config config) {
        // Your activation logic
    }

    // Configuration interface
    @ObjectClassDefinition(name = "Content Statistics Configuration")
    public @interface Config {

        @AttributeDefinition(name = "Sub Service Name", description = "The sub-service name")
        String subServiceName() default "wknd-examples-statistics";
    }
}

 

you can then use @OsgiServiceto mock and register the required service:

 

 

import org.apache.sling.serviceusermapping.ServiceUserMapped;
import org.junit.jupiter.api.Test;
import org.apache.sling.testing.mock.osgi.MockOsgi;

class ContentStatisticsImplTest {

    @Test
    void testContentStatistics() {
        // Mock the ServiceUserMapped service
        ServiceUserMapped wkndExamplesStatisticsService = MockOsgi
                .newService(ServiceUserMapped.class)
                .property("subServiceName", "wknd-examples-statistics")
                .build();

        // Register the mocked service
        MockOsgi.injectServices(new ContentStatisticsImpl(), wkndExamplesStatisticsService);

        // Your test logic here
    }
}

This approach allows you to simulate the OSGi environment and register the required services for your unit tests.

Thanks.