Expand my Community achievements bar.

SOLVED

ResourceBundleProvider cannot be injected in a JUnit testing context

Avatar

Level 2

Greetings,

 

I am injecting a ResourceBundleProvider into a model as shown below.  This works great, but I'm having difficulty writing a JUnit test for this class because I can't find a way to populate the ResourceBundleProvider during test execution.  I am certain the problem is related to the Filter annotation because if I remove it, the test will work (but then the field will be null during live execution).

 

 

// PeopleAggregationImpl.java

import javax.inject.Inject;
import org.apache.sling.i18n.ResourceBundleProvider;
import org.apache.sling.models.annotations.Filter;

@Model(
        adaptables = { Resource.class },
        adapters = { PeopleAggregation.class },
        resourceType = { PeopleAggregationImpl.RESOURCE_TYPE },
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
        )
public class PeopleAggregationImpl extends BaseComponentImpl implements PeopleAggregation {

    @OSGiService
    private GeographicLocationService geographicLocationService;

    @inject
    @filter("(component.name=org.apache.sling.i18n.impl.JcrResourceBundleProvider)")
    private ResourceBundleProvider resourceBundleProvider;

    private String geoCountryCodes;

    
    public String getGeoCountryCodes() {
        return geoCountryCodes;
    }

    
    @PostConstruct
    public void init() {
        Locale languageLocale = ComponentModelUtil.getLocale(resource);
        geoCountryCodes = getGeoCountryCodesJson(languageLocale);
    }

    private String getGeoCountryCodesJson(Locale locale) {
        JsonArray countryCodeArray = new JsonArray();
        if (resourceBundleProvider != null) {
            final ResourceBundle resourceBundle = resourceBundleProvider.getResourceBundle(locale);
            I18n i18n = new I18n(resourceBundle);
            Map<String, String> countryMap = geographicLocationService.getGeoLocationCountryMap();
            for(Map.Entry<String, String> entry : countryMap.entrySet()) {
                JsonObject countryObject = new JsonObject();
                countryObject.addProperty("countryCode", entry.getValue().toLowerCase());
                countryObject.addProperty("countryName", i18n.get(entry.getKey()));
                countryCodeArray.add(countryObject);
            }
        }
        return countryCodeArray.toString();
    }
}

 

 

I've tried many techniques including creating mock ResourceBundleProviders and mock JcrResourceBundleProviders, but nothing has worked.  The example below demonstrates just one of these attempts.

 

 

// PeopleAggregationImplTest.java

import org.apache.sling.i18n.ResourceBundleProvider;
import org.apache.sling.i18n.impl.JcrResourceBundleProvider;
import org.junit.Rule;
import io.wcm.testing.mock.aem.junit.AemContext;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import com.tnc.aem.models.PeopleAggregation;

@RunWith(MockitoJUnitRunner.class)
public class PeopleAggregationImplTest {
   
    private static final String RESOURCE_PATH = "/content/page/jcr:content/tab_container/people_aggregation";

    private ResourceBundleProvider resourceBundleProvider = Mockito.mock(JcrResourceBundleProvider.class);

    @Rule
    public final AemContext ctx = new AemContext(ResourceResolverType.JCR_MOCK);

    @Before
    public void setUp() throws Exception {
        ctx.addModelsForClasses(PeopleAggregationImpl.class);
        ctx.registerService(ResourceBundleProvider.class, resourceBundleProvider);
    }

    
    public void testPeopleAggregation() throws NoSuchFieldException{
        Resource resource = ctx.resourceResolver().getResource(RESOURCE_PATH);
        PeopleAggregation peopleAggregation = resource.adaptTo(PeopleAggregation.class);
    }

 

 

Please let me know if you have any ideas how I can populate the ResourceBundleProvider dependency.

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

I am not sure why are you using 

@filter("(component.name=org.apache.sling.i18n.impl.JcrResourceBundleProvider)")

in your code as it is not recommended to be used. I have seen some developers are using ResourceBundleProvider to get the just locale to work with some translation stuff if this is your requirement then there are many ways to get site locale.

No coming to your context question you can easily get if from slingContext request.

like:

 

@Rule
public SlingContext context = new SlingContext();

@test
public void testGetResourceBundleFromRequest() {
ResourceBundle bundle = context.request().getResourceBundle(Locale.CANADA_FRENCH);
assertEquals(Locale.CANADA_FRENCH, bundle.getLocale());
assertNull(((MockResourceBundle) bundle).getBaseName());
}

 

Hope this helps

Umesh Thakur

View solution in original post

4 Replies

Avatar

Community Advisor

Hi


Ideally, you should refactor that sling model, what are you trying to do with the resourceBundleProvider? The @filter annotation is not recommended to be used: https://sling.apache.org/documentation/bundles/models.html#discouraged-annotations If you provide more insights we can guide you on the best approach.

 

 



Esteban Bustamante

Avatar

Level 2

Esteban,

Thank you for the reply.  I have updated the sample code in the original question to more clearly illustrate the use of the ResourceBundleProvider.  I am using the Inject/Filter annotations because, for reasons I do not understand, the @OSGiService annotation does not work for ResourceBundleProvider.  I could alternatively get a resource bundle by adapting this model from a SlingHttpServletRequest, but that seems inappropriate given that this model represents a component resource, not a servlet request.

Avatar

Correct answer by
Community Advisor

I am not sure why are you using 

@filter("(component.name=org.apache.sling.i18n.impl.JcrResourceBundleProvider)")

in your code as it is not recommended to be used. I have seen some developers are using ResourceBundleProvider to get the just locale to work with some translation stuff if this is your requirement then there are many ways to get site locale.

No coming to your context question you can easily get if from slingContext request.

like:

 

@Rule
public SlingContext context = new SlingContext();

@test
public void testGetResourceBundleFromRequest() {
ResourceBundle bundle = context.request().getResourceBundle(Locale.CANADA_FRENCH);
assertEquals(Locale.CANADA_FRENCH, bundle.getLocale());
assertNull(((MockResourceBundle) bundle).getBaseName());
}

 

Hope this helps

Umesh Thakur

Avatar

Administrator

@johnagordon83 Did you find the suggestions from users helpful? Please let us know if you require more information. Otherwise, please mark the answer as correct for posterity. If you have found out solution yourself, please share it with the community.



Kautuk Sahni