Ich versuche, Unit-Testfälle für jeden möglichen Codeabschnitt zu integrieren ..__ Ich habe jedoch Probleme beim Hinzufügen von Testfällen für API-Aufrufe, die durch Nachrüstung erstellt werden.
Der JUnit-Compiler führt niemals den Code in den CallBack-Funktionen aus.
Es gibt eine andere Möglichkeit, alle api-Aufrufe Synchronous zu Testzwecken durchzuführen, aber das ist nicht in jedem Fall in meiner App möglich.
Wie kann ich das klären? Ich muss auf jeden Fall Testfälle in den API-Aufrufen hinzufügen.
Ich teste meine Retrofit-Callbacks mit den Bibliotheken Mockito, Robolectric und Hamcrest.
Richten Sie zunächst den lib-Stack im build.gradle Ihres Moduls ein:
dependencies {
testCompile 'org.robolectric:robolectric:3.0'
testCompile "org.mockito:mockito-core:1.10.19"
androidTestCompile 'org.hamcrest:hamcrest-library:1.1'
}
Fügen Sie in globalem build.gradle des Jour-Projekts die folgende Zeile zu buildscript-Abhängigkeiten hinzu:
classpath 'org.robolectric:robolectric-gradle-plugin:1.0.1'
Rufen Sie dann das Menü "Build Variants" in Android Studio auf (um es schnell zu finden, drücken Sie Strg + Umschalt + A und suchen Sie danach) und schalten Sie die Option "Artefakt testen" auf "Komponententests". Android Studio wechselt Ihren Testordner zu "com.your.package (test)" (anstelle von androidTest).
OK. Das Setup ist fertig, Zeit, einige Tests zu schreiben!
Nehmen wir an, Sie haben einige Retrofit-API-Aufrufe, um eine Liste der Objekte abzurufen, die in einen Adapter für eine RecyclerView usw. eingefügt werden müssen. Wir möchten testen, ob der Adapter bei einem erfolgreichen Aufruf mit den richtigen Elementen gefüllt wird Dazu müssen wir Ihre Retrofit-Schnittstellenimplementierung ändern, die Sie verwenden, um mit einem Mock Anrufe zu tätigen, und einige falsche Antworten unter Verwendung der Mockito ArgumentCaptor-Klasse durchführen.
@Config(constants = BuildConfig.class, sdk = 21,
manifest = "app/src/main/AndroidManifest.xml")
@RunWith(RobolectricGradleTestRunner.class)
public class RetrofitCallTest {
private MainActivity mainActivity;
@Mock
private RetrofitApi mockRetrofitApiImpl;
@Captor
private ArgumentCaptor<Callback<List<YourObject>>> callbackArgumentCaptor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ActivityController<MainActivity> controller = Robolectric.buildActivity(MainActivity.class);
mainActivity = controller.get();
// Then we need to swap the retrofit api impl. with a mock one
// I usually store my Retrofit api impl as a static singleton in class RestClient, hence:
RestClient.setApi(mockRetrofitApiImpl);
controller.create();
}
@Test
public void shouldFillAdapter() throws Exception {
Mockito.verify(mockRetrofitApiImpl)
.getYourObject(callbackArgumentCaptor.capture());
int objectsQuantity = 10;
List<YourObject> list = new ArrayList<YourObject>();
for(int i = 0; i < objectsQuantity; ++i) {
list.add(new YourObject());
}
callbackArgumentCaptor.getValue().success(list, null);
YourAdapter yourAdapter = mainActivity.getAdapter(); // Obtain adapter
// Simple test check if adapter has as many items as put into response
assertThat(yourAdapter.getItemCount(), equalTo(objectsQuantity));
}
}
Fahren Sie mit dem Test fort, indem Sie mit der rechten Maustaste auf die Testklasse klicken und auf Ausführen klicken.
Und das ist es. Ich empfehle dringend die Verwendung von Robolectric (mit robolectric Gradle Plugin) und Mockito. Diese Libs machen das Testen von Android-Apps um einiges einfacher. Ich habe diese Methode aus dem folgenden blog post gelernt. Siehe auch diese Antwort .
Update: Wenn Sie Retrofit mit RxJava verwenden, überprüfen Sie meine andere Antwort dazu auch.
Wenn Sie .execute () anstelle von .enqueue () verwenden, wird die Ausführung synchron ausgeführt, sodass die Tests ordnungsgemäß ausgeführt werden können, ohne dass 3 verschiedene Bibliotheken importiert und Code hinzugefügt oder die Build-Varianten geändert werden müssen.
Mögen:
public class LoginAPITest {
@Test
public void login_Success() {
APIEndpoints apiEndpoints = RetrofitHelper.getTesterInstance().create(APIEndpoints.class);
Call<AuthResponse> call = apiEndpoints.postLogin();
try {
//Magic is here at .execute() instead of .enqueue()
Response<AuthResponse> response = call.execute();
AuthResponse authResponse = response.body();
assertTrue(response.isSuccessful() && authResponse.getBearer().startsWith("TestBearer"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Das JUnit-Framework führt niemals den Code in den CallBack-Funktionen aus, da der Haupt-Thread der Ausführung beendet wird, bevor die Antwort abgerufen wird. Sie können CountDownLatch
wie folgt verwenden:
@Test
public void testApiResponse() {
CountDownLatch latch = new CountDownLatch(1);
mApiHelper.loadDataFromBackend(new Callback() {
@Override
public void onResponse(Call call, Response response) {
System.out.println("Success");
latch.countDown();
}
@Override
public void onFailure(Call call, Throwable t) {
System.out.println("Failure");
latch.countDown();
}
});
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Dieses Testbeispiel kann auch hilfreich sein.
wenn bereits kapseln retrofit2.0 mit rx mit restful
open class BaseEntity<E> : Serializable {
/*result code*/
var status: Int = 0
/**data */
var content: E? = null
}
und Server-API-Anfrage wie
@GET(api/url)
fun getData():Observable<BaseEntity<Bean>>
ihr Service rufen Sie zurück, nur eine Synchronisierungsanfrage
val it = service.getData().blockingSingle()
assertTrue(it.status == SUCCESS_CODE)