0

I am writing unit test cases for following class which extends WCMUsePOJO. Now, this class is using a getSlingScriptHelper method shown below.

public class ConstantsServiceProvider extends WCMUsePojo {
private static final Logger logger = LoggerFactory.getLogger(ConstantsServiceProvider.class);

private String var1;

@Override
public void activate() throws Exception {
    ConstantsService constantsService = getSlingScriptHelper().getService(ConstantsService.class);
    if(constantsService != null) {
       var1  = constantsService.getVar1();
    }
}

public string getVar1() { return var1; }

}

The question is how do I mock getSlingScriptHelper method? Following is my unit test code.

public class ConstantsServiceProviderTest {

@Rule
public final SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK);

@Mock
public SlingScriptHelper scriptHelper;

public ConstantsServiceProviderTest() throws Exception {

}

@Before
public void setUp() throws Exception {
    ConstantsService service = new ConstantsService();
    scriptHelper = context.slingScriptHelper();
    provider = new ConstantsServiceProvider();

    provider.activate();
}

@Test
public void testGetvar1() throws Exception {
    String testvar1 = "";
    String var1 = provider.getVar1();
    assertEquals(testvar1, var1);
}
} 
romie99
  • 305
  • 3
  • 4
  • 18

3 Answers3

3

The only thing that you should "have to"* mock is the SlingScriptHelper instance itself, so that it will mimic the dependency injection of the declared service.

Everything else (e.g. the Bindings instance) can be a concrete implementation, for example:

import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.api.scripting.SlingScriptHelper;
import org.junit.Test;

import javax.script.Bindings;
import javax.script.SimpleBindings;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ConstantsServiceProviderTest {

    private SlingScriptHelper mockSling = mock(SlingScriptHelper.class);
    private ConstantsServiceProvider constantsServiceProvider = new ConstantsServiceProvider();
    private Bindings bindings = new SimpleBindings();

    @Test
    public void testFoo() throws Exception {
        //Arrange
        final String expected = "Hello world";
        final ConstantsService testConstantsService = new TestConstantsService(expected);
        when(mockSling.getService(ConstantsService.class)).thenReturn(testConstantsService);
        bindings.put(SlingBindings.SLING, mockSling);

        //Act
        constantsServiceProvider.init(bindings);

        //Assert
        final String actual = constantsServiceProvider.getVar1();
        assertThat(actual, is(equalTo(expected)));
    }

    class TestConstantsService extends ConstantsService {
        String var1 = "";

        TestConstantsService(String var1) {
            this.var1 = var1;
        }

        @Override
        String getVar1() {
            return var1;
        }
    }

}

The entry point here, as you said above, is via the init() method of the WCMUsePojo superclass (as this method is an implementation of the Use.class interface, this test structure also works for testing that via that interface, even if you don't use WCMUsePojo directly.)

*this could be any type of test-double, not necessarily a mock.

anotherdave
  • 6,656
  • 4
  • 34
  • 65
2

You shouldn't create a mock for ConstantsServiceProvider.class if you want to unit-test it. Instead, you should create mocks of its internal objects. So:

  1. Create real instance of ConstantsServiceProvider with new
  2. Mock objects that are returned by getSlingScriptHelper().getService(.) methods. Usually, dependencies are provided (injected) to classes by some container like Spring or simply provided by other classes of your app using setters. In both cases mocks creation is easy.
  3. If your current implementation doesn't allow this - consider refactoring.
  4. You are testing void activate() method which doesn't return anything. So, you should verify calling constantsService.getVar1() method.

I strongly advice you to study Vogella unit-testing tutorial

Ivan Pronin
  • 1,768
  • 16
  • 14
2

Here one of possible solution. The main idea is to have a real object of your class but with overridden getSlingScriptHelper() to return mocked scriptHelper.

I mocked the ConstantsService as well but may be not needed, I don't know your code.

public class ConstantsServiceProviderTest {
    @Mock
    public SlingScriptHelper scriptHelper;

    @Test
    public void getVar1ReturnsActivatedValue() throws Exception {
        // setup
        final String expectedResult = "some value";

        // Have a mocked ConstantsService, but if possible have a real instance.
        final ConstantsService mockedConstantsService = 
            Mockito.mock(ConstantsService.class);
        Mockito.when(
            mockedConstantsService.getVar1())
                .thenReturn(expectedResult);

        Mockito.when(
            scriptHelper.getService(ConstantsService.class))
                .thenReturn(mockedConstantsService);

        // Have a real instance of your class under testing but with overridden getSlingScriptHelper()
        final ConstantsServiceProvider providerWithMockedHelper =
            new ConstantsServiceProvider() {
                @Override
                SlingScriptHelper getSlingScriptHelper() {
                    return scriptHelper;
                }
            };

        // when
        String actualResult = providerWithMockedHelper.getVar1();

        // then
        assertEquals(expectedResult, actualResult);
    }
}
Dmytro Maslenko
  • 2,247
  • 9
  • 16
  • Thank you so much. I was able to get slingscripthelper working. and my method is very similar to your solution except for one extra addition. I had to mock the Bindings class as well. ` // configure sling script binding.` `Bindings bindings = mock(Bindings.class);` `when(bindings.get("sling")).thenReturn(context.slingScriptHelper());` add this and then call `provider.init(bindings)` method. this will eventually runs the activate method as it is called inside **init()** already. – romie99 Aug 14 '17 at 17:47
  • You may add this to your solution if you feel it is right. I will mark your answer as the correct one. :) – romie99 Aug 14 '17 at 17:51
  • Yes, on your real project you should have correct version. Here on Stackoverflow some details may be missed because the goal is to provide the main idea, to unblock the author. In your case I didn't know about `init()` and `Binding`, because it was missed in the original your post, so, I propose left all 'as is' to be consistent with your question. – Dmytro Maslenko Aug 14 '17 at 18:01