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.
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");
}
...
}
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With