gradlerobolectricrobolectric-gradle-plugin

Using multiple res folders with Robolectric


My current Gradle configuration has multiple (Merged) res folders:

sourceSets {
    androidTest {
        setRoot('src/test')
    }
    main {
        res.srcDirs =
            [
                'src/main/res/features/registration',
                'src/main/res/features/login',
                'src/main/res'
            ]
    }
}

But Robolectric allows me to configure a single directory using AndroidManifest:

public class RobolectricGradleTestRunner extends RobolectricTestRunner {
    private static final int MAX_SDK_SUPPORTED_BY_ROBOLECTRIC = 18;

    public RobolectricGradleTestRunner(Class<?> testClass) throws InitializationError {
        super(testClass);
    }

    @Override
    protected AndroidManifest getAppManifest(Config config) {
        String manifestProperty = "../app/src/main/AndroidManifest.xml";
        String resProperty = "../app/src/main/res";

        return new AndroidManifest(Fs.fileFromPath(manifestProperty), Fs.fileFromPath(resProperty)) {
            @Override
            public int getTargetSdkVersion() {
                return MAX_SDK_SUPPORTED_BY_ROBOLECTRIC;
            }
        };
    }
}

This way tests are failing. Is it possible to configure robolectric to reflect my gradle file?


Solution

  • Ok, this is the easiest way to do it, You will have to extend RobolectricTestRunner getAppManifest and createAppResourceLoader.

    In getAppManifest you will simply have to store the manifest in a field, let's say mDefaultManifest.

    In createAppResourceLoader you will have to add the right resources injected.

    /**
     * TODO: Watch OUT this is copied from RobolectricTestRunner in Robolectric-2.4 keep it up to date!
     */
    @Override
    protected ResourceLoader createAppResourceLoader(ResourceLoader systemResourceLoader, AndroidManifest appManifest) {
        List<PackageResourceLoader> appAndLibraryResourceLoaders = new ArrayList<PackageResourceLoader>();
        for (ResourcePath resourcePath : appManifest.getIncludedResourcePaths()) {
            appAndLibraryResourceLoaders.add(createResourceLoader(resourcePath));
        }
    
            /* BEGIN EDIT */
            if(mDefaultManifest != null) {
                ResourcePath rpInjected = new ResourcePath(mDefaultManifest.getRClass(), mDefaultManifest.getPackageName(), Fs.fileFromPath("../app/src/main/res/features/registration"), mDefaultManifest.getAssetsDirectory());
                appAndLibraryResourceLoaders.add(createResourceLoader(rpInjected));
                rpInjected = new ResourcePath(mDefaultManifest.getRClass(), mDefaultManifest.getPackageName(), Fs.fileFromPath("../app/src/main/res/features/login"), mDefaultManifest.getAssetsDirectory());
                appAndLibraryResourceLoaders.add(createResourceLoader(rpInjected));
            }
            /* END EDIT */
    
        OverlayResourceLoader overlayResourceLoader = new OverlayResourceLoader(appManifest.getPackageName(), appAndLibraryResourceLoaders);
    
        Map<String, ResourceLoader> resourceLoaders = new HashMap<String, ResourceLoader>();
        resourceLoaders.put("android", systemResourceLoader);
        resourceLoaders.put(appManifest.getPackageName(), overlayResourceLoader);
        return new RoutingResourceLoader(resourceLoaders);
    }
    

    Do not forget to add @RunWith(YourTestRunner.class) in your test classes.