Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override the Robolectric runtime dependency repository URL?

We're trying to use the org.robolectric:robolectric:3.0 dependency from our own internal Nexus repository. The issue is that Robolectric tries to load some dependencies at runtime from a public repository (as mentioned here), and ignores any repository overrides in the build.gradle.

Since we don't have access to that public location from our intranet, my tests timeout after trying to load that dependency:

[WARNING] Unable to get resource 'org.robolectric:android-all:jar:5.0.0_r2-robolectric-1' from repository sonatype (https://oss.sonatype.org/content/groups/public/): Error transferring file: Operation timed out

The bottom section of the Robolectric configuration documentation recommends adding this to your Gradle configuration to override the URL:

android {
  testOptions {
    unitTests.all {
      systemProperty 'robolectric.dependency.repo.url', 'https://local-mirror/repo'
      systemProperty 'robolectric.dependency.repo.id', 'local'
    }
  }
}

Unfortunately, I've tested that and I never see that system property being set. I've printed it out from inside my custom Robolectric runner (which extends RobolectricGradleTestRunner) and that system property remains set to null.

System.out.println("robolectric.dependency.repo.url: " + System.getProperty("robolectric.dependency.repo.url"));

I also tried to do something similar to this comment (but that method doesn't exist to override in RobolectricGradleTestRunner), and I also tried setting the system properties directly in my custom Robolectric runner, and that didn't seem to help.

@Config(constants = BuildConfig.class)
public class CustomRobolectricRunner extends RobolectricGradleTestRunner {
    private static final String BUILD_OUTPUT = "build/intermediates";

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

        System.setProperty("robolectric.dependency.repo.url", "https://nexus.myinternaldomain.com/content");
        System.setProperty("robolectric.dependency.repo.id", "internal");

        System.out.println("robolectric.dependency.repo.url: " + System.getProperty("robolectric.dependency.repo.url"));
    }

The Robolectric source code does seem to confirm that these system properties exist.

like image 306
Dan J Avatar asked Sep 06 '16 22:09

Dan J


3 Answers

You can set properties mavenRepositoryId and mavenRepositoryUrl of RoboSettings which are used by MavenDependencyResolver.

Example:

public class CustomRobolectricRunner extends RobolectricGradleTestRunner {

    static {
        RoboSettings.setMavenRepositoryId("my-nexus");
        RoboSettings.setMavenRepositoryUrl("https://my-nexus.example.com/content/groups/public");
    }


    ...
}
like image 93
gar_r Avatar answered Nov 13 '22 18:11

gar_r


While not a fix for using the properties directly, another way to get this to work is by overriding getJarResolver() in a RobolectricTestRunner subclass and pointing it at your artifact host:

public final class MyTestRunner extends RobolectricTestRunner {
  public MyTestRunner(Class<?> testClass) throws InitializationError {
    super(testClass);
  }

  @Override protected DependencyResolver getJarResolver() {
    return new CustomDependencyResolver();
  }

  static final class CustomDependencyResolver implements DependencyResolver {
    private final Project project = new Project();

    @Override public URL[] getLocalArtifactUrls(DependencyJar... dependencies) {
      DependenciesTask dependenciesTask = new DependenciesTask();
      RemoteRepository repository = new RemoteRepository();
      repository.setUrl("https://my-nexus.example.com/content/groups/public");
      repository.setId("my-nexus");
      dependenciesTask.addConfiguredRemoteRepository(repository);
      dependenciesTask.setProject(project);
      for (DependencyJar dependencyJar : dependencies) {
        Dependency dependency = new Dependency();
        dependency.setArtifactId(dependencyJar.getArtifactId());
        dependency.setGroupId(dependencyJar.getGroupId());
        dependency.setType(dependencyJar.getType());
        dependency.setVersion(dependencyJar.getVersion());
        if (dependencyJar.getClassifier() != null) {
          dependency.setClassifier(dependencyJar.getClassifier());
        }
        dependenciesTask.addDependency(dependency);
      }
      dependenciesTask.execute();

      @SuppressWarnings("unchecked")
      Hashtable<String, String> artifacts = project.getProperties();
      URL[] urls = new URL[dependencies.length];
      for (int i = 0; i < urls.length; i++) {
        try {
          urls[i] = Util.url(artifacts.get(key(dependencies[i])));
        } catch (MalformedURLException e) {
          throw new RuntimeException(e);
        }
      }
      return urls;
    }

    @Override public URL getLocalArtifactUrl(DependencyJar dependency) {
      URL[] urls = getLocalArtifactUrls(dependency);
      if (urls.length > 0) {
        return urls[0];
      }
      return null;
    }

    private String key(DependencyJar dependency) {
      String key =
          dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getType();
      if (dependency.getClassifier() != null) {
        key += ":" + dependency.getClassifier();
      }
      return key;
    }
  }
}

It should be noted that this relies on two internal classes of Robolectric so care should be taken when upgrading versions.

like image 7
Jake Wharton Avatar answered Nov 13 '22 18:11

Jake Wharton


As per the linked Github issue, one fix is to configure a settings.xml in your ~\.m2 folder:

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
    <mirrors>
        <mirror>
            <id>jcenter</id>
            <name>JCenter Remote</name>
            <mirrorOf>*</mirrorOf>
            <url>https://www.example.com/artifactory/jcenter-remote/</url>
        </mirror>
    </mirrors>
</settings>

<mirrorOf>*</mirrorOf> seems necessary to force Maven to redirect all repository requests to the one remote. See here for more details about mirror settings in Maven.

I found that using a remote of Sonatype is not sufficient, you should use a remote of JCenter or Maven Central in order to obtain all of the transitive dependencies.

like image 1
David Rawson Avatar answered Nov 13 '22 18:11

David Rawson