Search code examples
mockingadobeaemsling

AEM 6.3, wcm.io: register (Mock)ResourceResolverFactory issue


I am using wcm.io to create an AEMContext and I am able to the a simple Sling model that has no reference to other services. I do want to add a reference to my AliasService so I register/activate it in my context setup:

aemContext.registerInjectActivateService(new AliasService());

AliasService:

public class AliasService {

@Reference
private AutoClosableResourceResolverFactory resourceResolverFactory;

...

As you can see this service has a reference to AutoClosableResourceResolverFactory so I register/activate is as well:

aemContext.registerInjectActivateService(AutoClosableResourceResolverFactory.class, new AutoClosableResourceResolverFactory());

AutoClosableResourceResolverFactory:

public class AutoClosableResourceResolverFactory {

@Reference
private ResourceResolverFactory delegate;

...

The service depends on ResourceResolverFactory so I inject/activate it using the 'MockResourceResolverFactory' (final list of injects):

aemContext.registerInjectActivateService(ResourceResolverFactory.class, new MockResourceResolverFactory());
aemContext.registerInjectActivateService(AutoClosableResourceResolverFactory.class, new AutoClosableResourceResolverFactory());
aemContext.registerInjectActivateService(new AliasService());

I now get the following error if I try to execute my test:

Caused by: java.lang.IllegalArgumentException: args must be an even number of name/values:[org.apache.sling.testing.resourceresolver.MockResourceResolverFactory@7fb95505]

enter image description here

I have no idea how to fix this or what is causing this, all help is greatly appreciated!

UPDATE

The fix Jens provided totally worked for my older services (Felix annotation based -> org.apache.felix.scr.annotations.Component). When I try this for a newer service (OSGI annotation based -> org.osgi.service.component.annotations.Component), I get an error.

Service:

...
import org.osgi.service.component.annotations.Component;

@Component(service = TestService.class, immediate = true, property = {
    Constants.SERVICE_DESCRIPTION + "=" + "Test Service",
    Constants.SERVICE_VENDOR + "=" + BundleConstants.SERVICE_VENDOR
})
public class TestServiceImpl implements TestService {

@Override
public String getValue() {
    return "test-value-from-service";
   }
}

Sling Model:

@OSGiService
private TestService testService;

public String getValue(){
   return testService.getValue();
}

Register:

TestService testService = aemContext.registerInjectActivateService(new TestServiceImpl());

Error:

Caused by: org.apache.sling.testing.mock.osgi.NoScrMetadataException: No OSGi SCR metadata found for class com.asadventure.core.service.TestServiceImpl

enter image description here

What am I missing?


Solution

  • You do not need to bother with the ResourceResolverFactory. It is already registered in the AemContext.

    This should be the solution for your problem:

    public class AliasServiceTest {
    
        @Rule
        public final AemContext aemContext = new AemContext();
    
        @Test
        public void testAliasService() {
            // This will register your Resource Resolver Factory. This should work 
            // because the referenced (Sling) Resource Resolver Factory is already 
            // registered in the "aemContext". Therefore, all references are satisfied
            // and the service should be activated.
            aemContext.registerInjectActivateService(new AutoClosableResourceResolverFactory());
            AliasService service = aemContext.registerInjectActivateService(new AliasService());
    
            service.callSomeMethod();
        }
    
    }
    

    You can move the registration of your AutoClosableResourceResolverFactory to a "set up" method that is annotated by @Before. This way you do not have to this for every test. Alternatively, you can build your own context (which I suspect you are already doing judging by the stacktrace in your image) and register the service there.

    Furthermore, it seems that your code has an error:

    registerInjectActivateService(ResourceResolverFactory.class, new MockResourceResolverFactory());
    

    As far as I can tell there is no method signature that takes the Clazz as first argument and the service instance as second argument.

    See: http://wcm.io/testing/aem-mock/apidocs/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.html

    It is one of the following:

    registerInjectActivateService(T service)
    registerInjectActivateService(T service, Map<String,Object> properties)
    registerInjectActivateService(T service, Object... properties)
    

    Where T service is your actual service instance and the (optional) second argument is used for configuration. This configuration would be passed in the activate method of your service.