Testowanie aktywności w aplikacji

Aktywności służą jako kontenery na każdą interakcję użytkownika w aplikacji, dlatego ważne jest, aby sprawdzać, jak te działania zachowują się w trakcie zdarzeń na poziomie urządzenia, takich jak:

  • Inna aplikacja, na przykład aplikacja Telefon na urządzeniu, przerywa aktywność w aplikacji.
  • System niszczy i odtwarza Twoją aktywność.
  • Użytkownik umieści Twoją aktywność w nowym środowisku okiennym, takim jak obraz w obrazie (PIP) lub wiele okien.

W szczególności musisz się upewnić, że aktywność działa prawidłowo w odpowiedzi na zdarzenia opisane w artykule Cykl życia aktywności.

Z tego przewodnika dowiesz się, jak ocenić zdolność aplikacji do zachowania integralności danych i zapewniania użytkownikom dobrych wrażeń, gdy działania aplikacji przechodzą przez różne stany w cyklu życia.

Dysk o stanie aktywności

Jednym z kluczowych aspektów testowania aktywności w aplikacji jest umieszczanie jej w konkretnych stanach. Aby zdefiniować tę „daną” część testów, użyj wystąpień narzędzia ActivityScenario, które wchodzi w skład biblioteki AndroidX Test. Za pomocą tych zajęć możesz przypisywać aktywności do stanów symulujących zdarzenia na poziomie urządzenia.

ActivityScenario to wieloplatformowy interfejs API, którego można używać zarówno w testach jednostkowych lokalnych, jak i testach integracji na urządzeniu. Na prawdziwym lub wirtualnym urządzeniu ActivityScenario zapewnia bezpieczeństwo wątków, synchronizuje zdarzenia między wątkiem narzędziowym testu a wątkiem, w którym testowana jest Twoja aktywność.

Interfejs API jest szczególnie przydatny do oceny zachowania testowanej aktywności po jej zniszczeniu lub utworzeniu. W tej sekcji przedstawiamy najczęstsze przypadki użycia związane z tym interfejsem API.

Utwórz aktywność

Aby utworzyć testowaną aktywność, dodaj kod widoczny w tym fragmencie:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
       launchActivity<MyActivity>().use {
       }
    }
}

Po utworzeniu aktywności ActivityScenario przenosi ją do stanu RESUMED. Ten stan oznacza, że aktywność jest aktywna i widoczna dla użytkowników. W tym stanie możesz wchodzić w interakcje z elementami View swojej aktywności za pomocą testów interfejsu Espresso.

Google zaleca wywołanie metody close po zakończeniu testu. Oczyszcza to powiązane zasoby i zwiększa stabilność testów. ActivityScenario implementuje Closeable, więc możesz zastosować rozszerzenie use lub try-with-resources w języku programowania Java, aby aktywność zamykała się automatycznie.

Możesz też użyć metody ActivityScenarioRule, aby automatycznie wywoływać metodę ActivityScenario.launch przed każdym testem i ActivityScenario.close w przypadku dezaktywacji testu. Z przykładu poniżej dowiesz się, jak zdefiniować regułę i wybrać z niej scenariusz:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @get:Rule var activityScenarioRule = activityScenarioRule<MyActivity>()

    @Test fun testEvent() {
        val scenario = activityScenarioRule.scenario
    }
}

Ustawianie nowego stanu aktywności

Aby zmienić stan aktywności, np. CREATED lub STARTED, wywołaj moveToState(). Symuluje to sytuację, w której aktywność zostaje wstrzymana lub wstrzymana przez inną aplikację lub działanie systemowe.

Przykład użycia uprawnienia moveToState() znajdziesz we fragmencie kodu:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        launchActivity<MyActivity>().use { scenario ->
            scenario.moveToState(State.CREATED)
        }
    }
}

Określanie bieżącego stanu aktywności

Aby określić bieżący stan testowanej aktywności, pobierz wartość pola state w obiekcie ActivityScenario. Szczególnie przydatne jest sprawdzenie stanu testowanej aktywności, czy przekierowuje ona użytkownika do innej aktywności lub kończy działanie, jak pokazano w tym fragmencie kodu:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        launchActivity<MyActivity>().use { scenario ->
            scenario.onActivity { activity ->
              startActivity(Intent(activity, MyOtherActivity::class.java))
            }

            val originalActivityState = scenario.state
        }
    }
}

Powtórz zadanie

Gdy na urządzeniu brakuje zasobów, system może zniszczyć aktywność i wymagać od aplikacji jej odtworzenia po powrocie użytkownika do aplikacji. Aby zasymulować te warunki, wywołaj recreate():

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        launchActivity<MyActivity>().use { scenario ->
            scenario.recreate()
        }
    }
}

Klasa ActivityScenario zachowuje zapisany stan instancji aktywności i wszystkie obiekty oznaczone za pomocą @NonConfigurationInstance. Obiekty te wczytują się do nowej instancji testowanej aktywności.

Pobieranie wyników aktywności

Aby uzyskać kod wyniku lub dane powiązane z zakończoną aktywnością, pobierz wartość pola result w obiekcie ActivityScenario, jak pokazano w tym fragmencie kodu:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testResult() {
        launchActivity<MyActivity>().use {
            onView(withId(R.id.finish_button)).perform(click())

            // Activity under test is now finished.

            val resultCode = scenario.result.resultCode
            val resultData = scenario.result.resultData
        }
    }
}

Aktywowanie działań w aktywności

Wszystkie metody w ActivityScenario blokują wywołania, więc interfejs API wymaga ich uruchomienia w wątku instrumentacji.

Aby aktywować działania w testowanej aktywności, użyj funkcji dopasowywania widoków Espresso do interakcji z elementami w widoku:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        launchActivity<MyActivity>().use {
            onView(withId(R.id.refresh)).perform(click())
        }
    }
}

Jeśli jednak chcesz wywoływać metodę w samej aktywności, możesz to bezpiecznie zrobić, implementując ActivityAction:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        launchActivity<MyActivity>().use { scenario ->
            scenario.onActivity { activity ->
              activity.handleSwipeToRefresh()
            }
        }
    }
}