Использование MockWebServer с Robolectric
Я пытаюсь тестировать некоторые вызовы API с помощью MockWebServer и Robolectric.
Мой тестовый класс помечен:
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 23)
Однако при попытке создать экземпляр Retrofit я получаю следующее исключение:
java.lang.NullPointerException
at android.os.Handler.__constructor__(Handler.java:229)
at android.os.Handler.<init>(Handler.java)
at retrofit2.Platform$Android$MainThreadExecutor.<init>(Platform.java:105)
at retrofit2.Platform$Android.defaultCallbackExecutor(Platform.java:97)
at retrofit2.Retrofit$Builder.build(Retrofit.java:556)
Код, который я использую для сборки модифицированного экземпляра:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(mMockServer.url(""))
.addConverterFactory(GsonConverterFactory.create())
.build();
Вышеуказанное исключение возвращается при вызове .build()
,
Как мне исправить эту проблему?
4 ответа
У меня была такая же проблема. Здесь открыта ошибка, связанная с основной причиной, но пока она продолжается, я решил использовать Robolectric 3.0 и решение, описанное здесь dave-r12, которое должно создать макет, который я включил ниже.
@RunWith(RobolectricGradleTestRunner.class)
@Config(shadows = CreateOkHttpClientTest.MyNetworkSecurityPolicy.class)
public class CreateOkHttpClientTest {
@Test
...
@Implements(NetworkSecurityPolicy.class)
public static class MyNetworkSecurityPolicy {
@Implementation
public static NetworkSecurityPolicy getInstance() {
try {
Class<?> shadow = MyNetworkSecurityPolicy.class.forName("android.security.NetworkSecurityPolicy");
return (NetworkSecurityPolicy) shadow.newInstance();
} catch (Exception e) {
throw new AssertionError();
}
}
@Implementation
public boolean isCleartextTrafficPermitted() {
return true;
}
}
}
Если вы застряли на Robolectric 3 или ниже и нацелены на API 25, правильное решение почти такое же, как и принятое.
@Implements(NetworkSecurityPolicy.class)
public class NetworkSecurityPolicyShadow {
@Implementation
public static NetworkSecurityPolicy getInstance() {
try {
Class<?> shadow = Class.forName("android.security.NetworkSecurityPolicy");
return (NetworkSecurityPolicy) shadow.newInstance();
} catch (Exception e) {
throw new AssertionError();
}
}
@Implementation
public boolean isCleartextTrafficPermitted(String host) {
return true;
}
}
Разница только в isCleartextTrafficPermitted(String host)
потому что OkHttp будет пытаться вызвать этот метод с host
аргумент.
Альтернативное решение, которое, кажется, работает, это просто предоставить фиктивного исполнителя при настройке Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.example.com/")
.client(client)
.callbackExecutor(new Executor() {
@Override
public void execute(Runnable command) {
//doesn't matter since tests are synchronous
}
})
.addConverterFactory(ScalarsConverterFactory.create())
.build();
Это может быть что-то, что в конце должно быть исправлено на стороне Модификации, каким-то образом обнаруживая, является ли текущая Платформа Robolectric и возвращает фиктивного исполнителя или что-то в этом роде.
Не могли бы вы попробовать с SDK =21. Я считаю, что поддержка SDK =23 была добавлена только из 3.1-SNAPSHOT, а не выпускной версии.
Изменить: я был неправ, поддержка была добавлена из версии 3.1, потому что я мог успешно запустить тест
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 23)
public class ExampleUnitTest {
private MockWebServer mMockServer = new MockWebServer();
@Test
public void build_retrofit() throws Exception {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(mMockServer.url(""))
.addConverterFactory(GsonConverterFactory.create())
.build();
}
}
Мой gradle.build
testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.1"
testCompile 'com.squareup.okhttp3:mockwebserver:3.3.0'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
Какую версию библиотеки вы используете?