Tworzenie testów jednostek lokalnych

Test lokalny działa bezpośrednio na Twojej stacji roboczej, a nie na urządzeniu z Androidem czy emulatorze. W związku z tym do testów jest wykorzystywana lokalna maszyna wirtualna Java (JVM), a nie urządzenie z Androidem. Testy lokalne umożliwiają szybsze ocenę logiki aplikacji. Jednak brak możliwości interakcji z platformą Android ogranicza liczbę dostępnych testów.

Test jednostkowy pozwala sprawdzić zachowanie niewielkiej części kodu – testowej jednostki. Aby to zrobić, uruchamia odpowiedni kod i sprawdza wynik.

Testy jednostkowe są zwykle proste, ale ich konfiguracja może sprawiać problemy, jeśli jednostka w trakcie testów nie została zaprojektowana pod kątem możliwości testowania:

  • Kod, który chcesz zweryfikować, musi być dostępny z poziomu testu. Na przykład nie można bezpośrednio testować metody prywatnej. Zamiast tego przetestujesz klasę przy użyciu publicznych interfejsów API.
  • Aby testy jednostkowe były przeprowadzane w izolacji, zależności jednostki w testach muszą zostać zastąpione komponentami, które kontrolujesz, takimi jak fałszywe lub inne meble podwójne. Problem dotyczy szczególnie tych, które są zależne od platformy Androida.

Aby dowiedzieć się więcej o typowych strategiach testowania jednostkowego na Androidzie, przeczytaj artykuł Co przetestować.

Lokalizacja testów lokalnych

Domyślnie pliki źródłowe dla testów jednostkowych znajdują się w regionie module-name/src/test/. Katalog ten jest już utworzony podczas tworzenia nowego projektu w Android Studio.

Dodawanie zależności testowych

Musisz też skonfigurować zależności testowe w projekcie, aby używać standardowych interfejsów API udostępnianych przez platformę testów JUnit.

Aby to zrobić, otwórz plik build.gradle modułu aplikacji i określ następujące biblioteki jako zależności. Użyj funkcji testImplementation, aby wskazać, że dotyczą one lokalnego zbioru źródłowego, a nie aplikacji:

dependencies {
  // Required -- JUnit 4 framework
  testImplementation "junit:junit:$jUnitVersion"
  // Optional -- Robolectric environment
  testImplementation "androidx.test:core:$androidXTestVersion"
  // Optional -- Mockito framework
  testImplementation "org.mockito:mockito-core:$mockitoVersion"
  // Optional -- mockito-kotlin
  testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
  // Optional -- Mockk framework
  testImplementation "io.mockk:mockk:$mockkVersion"
}

Tworzenie klasy testowej jednostki lokalnej

Zapisujesz klasę testową jednostki lokalnej jako klasę testową JUnit 4.

Aby to zrobić, utwórz klasę zawierającą co najmniej 1 metodę testowania, zwykle w obiekcie module-name/src/test/. Metoda testowania zaczyna się od adnotacji @Test i zawiera kod do ćwiczenia i weryfikacji jednego aspektu komponentu, który chcesz przetestować.

Przykład poniżej pokazuje, jak wdrożyć klasę testową jednostki lokalnej. Metoda testowa emailValidator_correctEmailSimple_returnsTrue()próbuje zweryfikować metodę isValidEmail(), która jest metodą stosowaną w aplikacji. Funkcja testowa zwróci wartość „true” (prawda), jeśli isValidEmail() również zwróci wartość „true” (prawda).

Kotlin


import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

class EmailValidatorTest {
  @Test fun emailValidator_CorrectEmailSimple_ReturnsTrue() {
    assertTrue(EmailValidator.isValidEmail("name@email.com"))
  }

}

Java


import org.junit.Test;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

class EmailValidatorTest {
  @Test
  public void emailValidator_CorrectEmailSimple_ReturnsTrue() {
    assertTrue(EmailValidator.isValidEmail("name@email.com"));
  }
}

Należy utworzyć czytelne testy, które oceniają, czy komponenty aplikacji zwracają oczekiwane wyniki. Zalecamy korzystanie z biblioteki asercji, takiej jak junit.Assert, Hamcrest lub Truth. Powyższy fragment kodu to przykład użycia właściwości junit.Assert.

Pozorowana biblioteka Androida

Gdy przeprowadzasz testy jednostkowe, wtyczka Androida do obsługi Gradle zawiera bibliotekę, która zawiera wszystkie interfejsy API platformy Androida zgodne z wersją używaną w Twoim projekcie. W bibliotece znajdują się wszystkie metody i klasy publiczne tych interfejsów API, ale kod wewnątrz metod został usunięty. Jeśli dostępna jest któraś z metod, test zgłasza wyjątek.

Dzięki temu można tworzyć testy lokalne, odwołując się do klas w platformie Androida, takiej jak Context. Co ważne, w klasach Androida można wykorzystać model do żartów.

Imitowanie zależności Androida

Typowym problemem jest zauważenie, że klasa korzysta z zasobu tekstowego. Zasoby z ciągami znaków możesz uzyskać, wywołując metodę getString() w klasie Context. W teście lokalnym nie można jednak używać metody Context ani żadnej z jej metod, ponieważ należą one do platformy Androida. W idealnej sytuacji wywołanie getString() zostałoby usunięte z zajęć, ale nie zawsze jest to praktyczne. Rozwiązaniem jest utworzenie symulowanego interfejsu Context, który zawsze zwraca tę samą wartość po wywołaniu metody getString().

Dzięki bibliotece mockable Android i platformom do żartów, takim jak Mockito czy MockK, możesz zaprogramować w testach jednostkowych działanie próbek klas Androida.

Aby za pomocą Mockito dodać przykładowy obiekt do testu jednostki lokalnej, postępuj zgodnie z tym modelem programowania:

  1. Dodaj zależność z biblioteki Mockito do pliku build.gradle zgodnie z opisem w sekcji Konfigurowanie środowiska testowego.
  2. Na początku definicji klasy testu jednostkowego dodaj adnotację @RunWith(MockitoJUnitRunner.class). Ta adnotacja informuje usługę uruchamiającą testy Mockito, aby sprawdzić, czy użycie platformy jest poprawne, i upraszcza inicjowanie obiektów próbnych.
  3. Aby utworzyć pozorowany obiekt dla zależności Androida, dodaj adnotację @Mock przed deklaracją pola.
  4. Aby skrócić zachowanie zależności, możesz określić warunek i zwrócić wartość w przypadku jego spełnienia, korzystając z metod when() i thenReturn().

W poniższym przykładzie pokazano, jak utworzyć test jednostkowy, który wykorzystuje w kotlinie próbny obiekt Context utworzony za pomocą metody Mockito-Kotlin.

import android.content.Context
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock

private const val FAKE_STRING = "HELLO WORLD"

@RunWith(MockitoJUnitRunner::class)
class MockedContextTest {

  @Mock
  private lateinit var mockContext: Context

  @Test
  fun readStringFromContext_LocalizedString() {
    // Given a mocked Context injected into the object under test...
    val mockContext = mock<Context> {
        on { getString(R.string.name_label) } doReturn FAKE_STRING
    }

    val myObjectUnderTest = ClassUnderTest(mockContext)

    // ...when the string is returned from the object under test...
    val result: String = myObjectUnderTest.getName()

    // ...then the result should be the expected one.
    assertEquals(result, FAKE_STRING)
  }
}

Więcej informacji o korzystaniu z platformy Mockito znajdziesz w materiałach referencyjnych Mockito API i klasie SharedPreferencesHelperTest w przykładowym kodzie. Wypróbuj też ćwiczenia z programowania dotyczące testowania aplikacji na Androida.

Błąd: „Metoda ... to nie jest przykładowa”

Biblioteka Pozorna Androida zgłasza wyjątek, jeśli próbujesz uzyskać dostęp do którejkolwiek z jej metod za pomocą komunikatu Error: "Method ... not mocked.

Jeśli zgłoszone wyjątki stanowią problem dla testów, możesz zmienić działanie, tak aby metody zwracały wartość null lub zero w zależności od typu zwrotu. Aby to zrobić, dodaj tę konfigurację do pliku build.gradle najwyższego poziomu w Groovy:

android {
  ...
  testOptions {
    unitTests.returnDefaultValues = true
  }