Search code examples
osgiaemsling

Unregister a service in wcm.io's AEM Mocks


I am following the guide presented by wcm.io on the proper usage of AEM Mocks. Specifically, I am interested in unregistering a service. The documentation provides useful code for registering a service but nothing for unregistering. I have already looked at this similar question by flavio.donze but the solutions given to his question don't work in the case of AEM Context.

First, if I attempt to register a service as such:

ServiceReference ref = aemcontext.bundleContext().getServiceReference(MyServiceClass.class.getName());

It is registered in bundleContext but not in aemcontext. The ref object is indeed set, but aemcontext never truly holds the service and if I attempt to register a servlet that uses this service as such:

context.registerInjectActivateService(new MyServlet(), myParams);

This call fails because MyServlet references MyServiceClass. What does work is registering a service directly on aemcontext, as such:

aemcontext.registerService(MyServiceClass.class, new MyServiceClass());

However, when I then attempt to unregister this service as in the solutions provided to flavio.donze's question, neither solution works. I don't get an error and I suppose that something does happen to the ServiceRegistration<MyServiceClass> reg, but the service is never unregistered from the aemcontext. If I attempt to register it again afterwards with the second command above, I get an error indicating that:

Multiple matches found for unary reference 'myServiceClass' for class...

Can anyone please help me? Is there a quick way to unregister a service through AEM Mocks?

Thanks!


Solution

  • Based on your comment I updated the answer:

    1. Then just do 2 test methods. The context object is re-initialized before each test method

    2. The right order to register services is important. Use registerService to register all Mock-services (e.g. created with Mockito). Then use registerInjectActivateService for all services that should be part of the JUnit-test. Because for registerInjectActivateService all referenced services must have been registered before.

    3. You don't need to mock the SlingSettingsService. This service is already part of Sling Mocks - and changing the run-mode is supported too.

    Here is an example OSGi service:

    @Component(service = FakeService.class)
    public class FakeService {
    
        @Reference
        private SlingSettingsService slingSettingsService;
    
        public boolean isAuthor() {
            return slingSettingsService.getRunModes().contains("author");
        }
    
        public boolean isPublish() {
            return slingSettingsService.getRunModes().contains("publish");
        }
    }
    

    Here is an JUnit test for this service:

    public class FakeTest {
    
        @Rule
        public final AemContext aemcontext = new AemContext();
    
        private FakeService fakeService;
    
        @Before
        public void setup() {
            fakeService = aemcontext.registerInjectActivateService(new FakeService());
        }
    
        @Test
        public void testOnAuthor() {
            aemcontext.runMode("author");
            assertTrue(fakeService.isAuthor());
            assertFalse(fakeService.isPublish());
        }
    
        @Test
        public void testOnPublish() {
            aemcontext.runMode("publish");
            assertFalse(fakeService.isAuthor());
            assertTrue(fakeService.isPublish());
        }
    }
    

    Old Answer

    1. You should not unregister a service in OSGi-Mocks (Sling-Mocks, AEM-Mocks). Why do you need this anyway? A Unit Test should test your functional code - but not that OSGi wiring is working. Just accept that you cannot verify everything in Unit test, even simple things like a non-starting bundle. Therefore you have health checks.

    2. If you really, really need this! You can extend the test framework. It is open source. And maybe others could use it too.

    3. Alternatively take a look into Pax Exam based Unit tests. In my opinion it is a little bit oversized for "normal" AEM projects. But it provides a real OSGi container. The big advantage is, that is can still run as a normal part of your maven build. In AEM projects I have seen such tests often introduced by technically interested guys. But in reality these tests only proved that you can have a real OSGi container in your Unit tests. Normal developers will not touch such tests, as it feels too complicated for them.

    4. Finally take a look in Server-side JUnit tests. The problem is, that you need an AEM instance and managed test data during your build. Also metrics like code coverage are difficult to measure. But I would recommend this anyway, if you have tests that heavily depend on AEM features. Tests with 50 lines of mocking a LiveRelationshipManager are often worthless. Then better run them server-side with a real AEM. Also the AEM project archetype already providing integration tests (= server-side JUnit tests). But even there you have to start/stop services, for testing WHAT?

    https://sling.apache.org/documentation/tutorials-how-tos/testing-sling-based-applications.html

    https://github.com/adobe/aem-project-archetype