Tworzenie testów jednostek lokalnych

Test lokalny odbywa się bezpośrednio na Twojej stacji roboczej, a nie na Androidzie urządzenia lub emulatora. Do testowania używa lokalnej maszyny wirtualnej Java (JVM), a nie urządzenia z Androidem. Testy lokalne umożliwiają szybsze sprawdzanie logiki aplikacji. Jednak brak możliwości interakcji Platforma Androida ogranicza liczbę dostępnych testów.

Test jednostek sprawdza zachowanie niewielkiej części kodu, jednostki test. Dzieje się to przez wykonanie tego kodu i sprawdzenie wyniku.

Testy jednostkowe są zwykle proste, ale ich konfiguracja może stanowić problem, jeśli jednostka w trakcie testowania nie jest zaprojektowany z myślą o możliwości testowania:

  • Kod, który chcesz zweryfikować, musi być dostępny w ramach testu. Na przykład nie możesz bezpośrednio testować metody prywatnej. Zamiast tego testujesz klasę za pomocą publicznych interfejsów API.
  • Aby można było przeprowadzić testy jednostkowe w izolacji, zależności jednostki w trakcie testów muszą zostać zastąpione komponentami, które kontrolujesz, takimi jak podróbki lub inne podwojenie testowe. Jest to szczególnie problematyczne, jeśli Twój kod zależy od platformy Android.

Aby dowiedzieć się więcej o popularnych strategiach testowania jednostkowego na Androida, przeczytaj artykuł Co testować.

Lokalizacja testów lokalnych

Domyślnie pliki źródłowe dla lokalnych testów jednostkowych są umieszczane w folderze module-name/src/test/ Ten katalog jest już utworzony, gdy tworzysz nowy projekt w Android Studio.

Dodaję zależności testowe

Musisz też skonfigurować w projekcie zależności testowe, aby używać parametru dostępnych w środowisku testowym JUnit.

Aby to zrobić, otwórz plik build.gradle modułu aplikacji i podaj następujące dane jako zależności. Użyj funkcji testImplementation, aby wskazać, że te dane dotyczą lokalnego testowego zestawu źródeł, 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 lokalnej klasy testów jednostkowych

Klasę testu jednostkowego lokalnego piszesz jako klasę testu JUnit 4.

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

Poniższy przykład pokazuje, jak wdrożyć klasę testu jednostkowego lokalnego. Metoda test emailValidator_correctEmailSimple_returnsTrue()próbuje zweryfikować isValidEmail(), która jest metodą w aplikacji. Funkcja test zwróci wartość true, jeśli isValidEmail() zwróci również wartość true.

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 tworzyć czytelne testy, które pozwolą ocenić, czy komponenty w tabeli zwróci oczekiwane wyniki. Zalecamy użycie biblioteki asercji, takiej jak junit.Assert, Hamcrest lub Truth. Powyższy fragment kodu pokazuje, jak za pomocą junit.Assert

Biblioteka Androida

Podczas wykonywania lokalnych testów jednostkowych wtyczka Gradle dla Androida zawiera bibliotekę z wszystkimi interfejsami API platformy Android w wersji zgodnej z wersją używaną w projekcie. W bibliotece znajdują się wszystkie publiczne metody tych interfejsów API, ale kod wewnątrz metod został usunięty. Jeśli tak, metody, które zostaną użyte, test zgłosi wyjątek.

Umożliwia to tworzenie testów lokalnych w przypadku odwołań do klas w Androidzie. takiej jak Context. Co ważniejsze, pozwala ono używać frameworka do symulowania w klasach Androida.

Pozorowanie zależności Androida

Typowym problemem jest to, że klasa używa ciągu znaków. Zasoby ciągu znaków możesz uzyskać, wywołując metodę getString() w klasie Context. W teście lokalnym nie można jednak używać właściwości Context ani żadnej z jej metod, ponieważ należą do platformy Androida. W idealnej sytuacji wywołanie do getString() zostałoby przeniesione poza klasę, ale nie zawsze jest to możliwe. Rozwiązaniem jest utworzenie mocka lub stuba Context, który zawsze zwraca tę samą wartość po wywołaniu metody getString().

Dzięki bibliotece Mockable na Androida i ramkom do emulacji takim jak Mockito czy MockK możesz programować zachowanie emulacji klas Androida w testach jednostkowych.

Aby dodać obiekt testowy do lokalnego testu jednostkowego za pomocą Mockito, postępuj zgodnie z tym modelem programowania:

  1. W pliku build.gradle uwzględnij bibliotekę Mockito, jak opisano w artykule Konfigurowanie środowiska testowego.
  2. Na początku definicji klasy testu jednostkowego dodaj @RunWith(MockitoJUnitRunner.class) adnotacja. Adnotacja informuje Mockito – uruchom testy do sprawdzenia, czy korzystasz z platformy prawidłowo upraszcza inicjowanie przykładowych obiektów.
  3. Aby utworzyć przykładowy obiekt zależności Androida, dodaj adnotację @Mock przed deklaracją pola.
  4. Aby skrócić zachowanie zależności, możesz określić warunek i zwracana wartość, gdy warunek jest spełniony za pomocą funkcji when() i thenReturn() .

Poniższy przykład pokazuje, jak utworzyć test jednostkowy, który używa obiektu mock Context w Kotlinie utworzonego za pomocą pakietu 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 ramowcu Mockito znajdziesz w dokumentacji interfejsu Mockito API oraz w klasie SharedPreferencesHelperTestprzykładowym kodzie. Wypróbuj też ćwiczenie z programowania dotyczące testowania aplikacji na Androida.

Błąd: „Metoda ... nie sfałszowana”

Biblioteka aplikacji na Androida zgłasza wyjątek, jeśli spróbujesz uzyskać dostęp do którejkolwiek z jej z komunikatem Error: "Method ... not mocked.

Jeśli zgłoszone wyjątki stanowią problem dla Twoich testów, możesz zmienić w taki sposób, aby metody zwracały wartość null lub zero, w zależności od zwracany typ. Aby to zrobić, dodaj w pliku build.gradle na najwyższym poziomie projektu w języku Groovy następującą konfigurację:

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