diff --git a/docs/Tutorial/Screenshot_tests_2.en.md b/docs/Tutorial/Screenshot_tests_2.en.md index b1ef9799e..b319c634b 100644 --- a/docs/Tutorial/Screenshot_tests_2.en.md +++ b/docs/Tutorial/Screenshot_tests_2.en.md @@ -1,19 +1,15 @@ # Screenshot-тесты. Часть 2. Установка стейтов и работа с ViewModel. -Продолжаем тему screenshot-тестов. - Если в вашем приложении планируется использование screenshot-тестов, то этот момент нужно учитывать не только при написании тестов, но также при разработке приложения. В сегодняшнем уроке мы поближе познакомимся с установкой стейтов, внесем изменения в код приложения, чтобы его можно было покрыть тестами, и напишем первый скриншот тест, в котором будет работа с ViewModel. ## Предварительные знания Если вы ранее не разрабатывали приложения под Android, то сегодняшний урок может быть сложным для понимания. Поэтому мы настоятельно рекомендуем перед прохождением данного урока ознакомиться со следующими темами: - +1. [Фрагменты](https://developer.android.com/guide/fragments) – что это, и как с ними работать +2. [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel) и шаблон проектирования MVVM +3. [StateFlow](https://developer.android.com/kotlin/flow/stateflow-and-sharedflow) +4. [Библиотека Mockk](https://mockk.io/) ## Обзор тестируемого приложения @@ -53,7 +49,7 @@ В пакете `screenshot_tests` создаем класс `LoadUserScreenshots` -Create class +Create class Наследуемся от `DocLocScreenshotTestCase` и передаем список языков в качестве параметра конструктору, сделаем скриншоты для английской и французской локалей @@ -87,12 +83,12 @@ class LoadUserScreenshots : DocLocScreenshotTestCase(locales = "en, fr") { Для того чтобы получить все состояния экрана мы будем, как и раньше, имитировать действия пользователя – кликать по кнопке и ждать получения результата. Создаем `PageObject` этого экрана. В пакете `com.kaspersky.kaspresso.tutorial.screen` добавляем класс `LoadUserScreen`, тип `Object` -Create page object +Create page object Наследумся от `KScreen` и добавляем все необходимые UI-элементы: кнопка загрузки, ProgressBar, TextView с именем пользователя и TextView с текстом ошибки - ```kotlin +```kotlin package com.kaspersky.kaspresso.tutorial.screen import com.kaspersky.kaspresso.screens.KScreen @@ -111,7 +107,6 @@ object LoadUserScreen : KScreen() { val username = KTextView { withId(R.id.username) } val error = KTextView { withId(R.id.error) } } - ``` Можем создавать скриншот-тест. Добавляем метод `takeScreenshots` @@ -164,7 +159,7 @@ class LoadUserScreenshots : DocLocScreenshotTestCase(locales = "en, fr") { } ``` -Далее, необходимо кликнуть по кнопку и сохранить снимок экрана в состоянии загрузки +Далее необходимо кликнуть по кнопке и сохранить снимок экрана в состоянии загрузки ```kotlin package com.kaspersky.kaspresso.tutorial.screenshot_tests @@ -266,8 +261,10 @@ class LoadUserScreenshots : DocLocScreenshotTestCase(locales = "en, fr") { } ``` -Таким образом, мы смогли написать скриншот тест, в котором получили все необходимые состояния экрана, имитируя действия пользователя – кликая по кнопке и ожидая результата выполнения запроса -Но давайте подумаем, насколько эта реализация подойдет для реальных приложений. + +## Проблемы текущего подхода + +Таким образом, мы смогли написать скриншот тест, в котором получили все необходимые состояния экрана, имитируя действия пользователя – кликая по кнопке и ожидая результата выполнения запроса. Но давайте подумаем, насколько эта реализация подойдет для реальных приложений. Если мы работаем с реальным приложением, то после клика на кнопку тест будет ждать, пока запрос не вернет какой-то ответ с сервера. Если интернет будет медленным, или на сервере будут наблюдаться какие-то проблемы, то и время ожидания ответа может сильно увеличиться, соответственно будет увеличено время выполнения теста. При этом обратите внимание, что тест будет выполнен для каждой локали, которую мы передали в качестве параметра конструктора `DocLocScreenshotTestCase`, и каждый из этих тестов будет зависеть от скорости интернет-соединения и от работоспособности сервера. @@ -290,13 +287,14 @@ class LoadUserScreenshots : DocLocScreenshotTestCase(locales = "en, fr") { На этом этапе важно понимать паттерн MVVM (Model-View-ViewModel). Если говорить кратко, то согласно этому паттерну в приложении логика отделяется от видимой части. Видимая часть, или можно сказать экраны (Activity и Fragments) отвечают за отображение элементов интерфейса и взаимодействия с пользователем. То есть они показывают вам какие-то элементы (кнопки, поля ввода и т.д.) и реагируют на действия пользователя (клики, свайпы и т.д). В паттерне MVVM эта часть называется View. + ViewModel в этом паттерне отвечает за логику. -Их взаимодействие выглядит следующим образом: ViewModel у себя хранит стейт экрана, она определяет, что следует показать пользователю. View получает этот стейт из ViewModel и в зависимости от полученного значения отрисовывает нужные элементы. Если пользователь выполняет какие-то действия, то View вызывает соответствующий метод из ViewModel. +Их взаимодействие выглядит следующим образом: ViewModel у себя хранит стейт экрана, она определяет, что следует показать пользователю. View получает этот стейт из ViewModel, и, в зависимости от полученного значения, отрисовывает нужные элементы. Если пользователь выполняет какие-то действия, то View вызывает соответствующий метод из ViewModel. Давайте посмотрим пример из нашего приложения. На экране есть кнопка загрузки, пользователь кликнул по ней, View вызывает метод загрузки данных из ViewModel. -Откройте класс `LoadUserFragment` из пакета `com.kaspersky.kaspresso.tutorial.user`. Этот фрагмент представляет собой View. В следующем фрагменте кода мы устанавливаем слушатель клика на кнопку и говорим, чтобы при клике на нее был вызвать метод `loadUser` из ViewModel +Откройте класс `LoadUserFragment` из пакета `com.kaspersky.kaspresso.tutorial.user`. Этот фрагмент в паттерне MVVM представляет собой View. В следующем фрагменте кода мы устанавливаем слушатель клика на кнопку и говорим, чтобы при клике на нее был вызван метод `loadUser` из ViewModel ```kotlin binding.loadingButton.setOnClickListener { @@ -322,7 +320,7 @@ fun loadUser() { } ``` -View (в данном случае фрагмент `LoadUserFragment`) подписывается на стейт из ViewModel и в зависимости от полученного значения меняет содержимое экрана. Происходит это в методе observeViewModel +View (в данном случае фрагмент `LoadUserFragment`) подписывается на стейт из ViewModel и в зависимости от полученного значения меняет содержимое экрана. Происходит это в методе `observeViewModel` ```kotlin private fun observeViewModel() { @@ -642,7 +640,7 @@ if (savedInstanceState == null) { ``` А для создания фрагмента внутри скриншот-тестов будем вызывать метод `newTestInstance`. -На данном этапе в метод `onViewCreated` мы присваиваем значение переменной `viewModel` независимо от того, используется этот фрагмент для скриншот-тестов или нет. Давайте это исправим, добавим поле `isForScreenshots` типа `Boolean`, по умолчанию установим значение `false`, а в методе `newTestInstance` установим значение `true`. +На данном этапе в методе `onViewCreated` мы присваиваем значение переменной `viewModel` независимо от того, используется этот фрагмент для скриншот-тестов или нет. Давайте это исправим, добавим поле `isForScreenshots` типа `Boolean`, по умолчанию установим значение `false`, а в методе `newTestInstance` установим значение `true`. ```kotlin package com.kaspersky.kaspresso.tutorial.user @@ -686,7 +684,7 @@ override fun onViewCreated(view: View, savedInstanceState: Bundle?) { } ``` -После создания вьюмодели мы устанавливаем слушатель клика на кнопку загрузки и в этом слушателе вызываем метод вьюмодели. В случае, если мы передали замоканный вариант ViewModel, вызов этого метода приведет к падению теста, так как у нее данный метод не реализован. Поэтому вызов любых методов вьюмодели мы также будем выполнять только в том случае, если этот фрагмент используется не для скриншот-тестов: +После создания вьюмодели мы устанавливаем слушатель клика на кнопку загрузки и в этом слушателе вызываем метод вьюмодели. В случае, если мы передали замоканный вариант ViewModel, вызов этого метода `viewModel.loadUser()` приведет к падению теста, так как у нее данный метод не реализован. Поэтому вызов любых методов вьюмодели мы также будем выполнять только в том случае, если этот фрагмент используется не для скриншот-тестов: ```kotlin override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -830,7 +828,7 @@ class LoadUserScreenshots : DocLocScreenshotTestCase(locales = "en, fr") { С этого момента нам не нужно имитировать действия пользователя, мы просто устанавливаем необходимое состояние, фрагмент его отрисовывает, и мы делаем скриншот. -Если вы сейчас запустите тест, то увидите, что скриншоты всех состояний успешно сохраняются в папку на устройстве, и это происходит гораздо чаще, чем в предыдущем варианте теста. +Если вы сейчас запустите тест, то увидите, что скриншоты всех состояний успешно сохраняются в папку на устройстве, и это происходит гораздо быстрее, чем в предыдущем варианте теста. ## Меняем стиль @@ -838,7 +836,7 @@ class LoadUserScreenshots : DocLocScreenshotTestCase(locales = "en, fr") { Данная проблема решается очень просто – в качестве параметра в метод `launchFragmentInContainer` можно передать стиль, который должен использоваться внутри фрагмента, его можно найти в манифесте приложения -Style Передать этот стиль в метод `launchFragmentInContainer` можно следующим образом: