KA0708 — Тестирование

  • Запись изменена:08.12.2022
  • Post category:Публикации
  • Reading time:20 минут чтения

Вот и настал тот этап в разработке программного обеспечения, который заставляет вас хотеть откладывать, независимо от того, насколько важным вы знаете, что это на самом деле.

Нравится вам это или нет, наличие хорошего набора тестов — как автоматических, так и ручных — гарантирует качество вашего программного обеспечения. При использовании Kotlin Multiplatform у вас будет достаточно инструментов для написания тестов. Так что, если вы думаете о том, чтобы на этот раз упустить ситуацию, вам придется придумать другое оправдание. :]

Настройка зависимостей

Тестирование вашего кода в мире KMP осуществляется по той же схеме, с которой вы уже знакомы. Вы тестируете код в общем модуле. Вам также может понадобиться использовать механизм expect/actual . Имея это в виду, настройка зависимостей структурно такая же, как и с нетестовым кодом.

В начальном проекте откройте build.gradle.kts внутри общего модуля. В sourceSetsблоке добавьте блок для commonTestисходного набора после val commonMain by getting:

val commonTest by getting { dependencies { implementation(kotlin("test-common")) implementation(kotlin("test-annotations-common")) } }

Вы добавляете два модуля из kotlin.testбиблиотеки. Эта библиотека предоставляет аннотации для маркировки тестовых функций и набор служебных функций, необходимых для утверждений в тестах, независимо от используемой тестовой среды. В -commonназвании указано, что вы можете использовать их в своем общем мультиплатформенном коде. Выполните синхронизацию Gradle.

Как вы заявили выше, ваши тестовые коды будут находиться в папке commonTest . Создайте его как родственный каталог для commonMain , щелкнув правой кнопкой мыши папку src внутри общего модуля и выбрав New ▸ Directory . Как только вы начнете вводить commonTest , Android Studio предоставит вам автодополнение. Выберите commonTest/kotlin .

Рис. 8.1 — Создание нового каталога в Android Studio

Рис. 8.1 — Создание нового каталога в Android Studio

Рис. 8.2 — Android Studio предлагает назвать тестовую директорию

Рис. 8.2 — Android Studio предлагает назвать тестовую директорию

Примечание . Хотя это и не обязательно, рекомендуется иметь тестовые файлы в той же структуре пакета, что и основной код. Если вы хотите сделать это, введите commonTest/kotlin/com/raywenderlich/organize/presentation на предыдущем шаге или позже создайте вложенные каталоги вручную.

Затем создайте класс с именем RemindersViewModelTestвнутри каталога, который вы только что создали. Как следует из названия, этот класс будет иметь все тесты, связанные с RemindersViewModel.

Теперь пришло время создать самую первую тестовую функцию для приложения. Добавьте это во вновь созданный класс:

@Test fun testCreatingReminder() { }

Вы реализуете тело функции позже. Обратите внимание на @Testаннотацию. Он исходит из kotlin.testбиблиотеки, которую вы ранее добавили в качестве зависимости. Обязательно импортируйте необходимый пакет вверху файла, если Android Studio не сделала это автоматически: import kotlin.test.Test.

Как только вы добавляете @Testв класс функцию с аннотацией, Android Studio показывает кнопки запуска в области кода, чтобы вам было проще запускать тесты.

Рис. 8.3 — Кнопка запуска тестов в желобе кода

Рис. 8.3 — Кнопка запуска тестов в желобе кода

Вы можете запустить тесты, нажав на эти кнопки, используя команды в терминале или нажав сочетание клавиш Control-Shift-R на Mac или Control-Shift-F10 на Windows и Linux.

Рис. 8.4 – Выбор тестовой платформы

Рис. 8.4 – Выбор тестовой платформы

Выберите android (:testDebugUnitTest) , чтобы запустить тест в режиме отладки на Android.

Поздравляем! Вы успешно провели свой первый тест… или нет?

Рис. 8.5 — Первый тест не пройден

Рис. 8.5 — Первый тест не пройден

Если вы внимательно прочитаете журналы, то заметите, что компилятор не смог разрешить ссылки на файлы Test. Вот почему это произошло:

Как упоминалось ранее, kotlin.testбиблиотека предоставляет только тестовые аннотации независимо от используемой вами тестовой библиотеки. Когда вы просите систему запустить тест на Android, ей нужно найти тестовую библиотеку на этой платформе, чтобы запустить ваши тесты. Поскольку вы не определили никаких тестовых библиотек для целей JVM, он не смог разрешить аннотации, и тест завершился неудачно. В результате следующим шагом будет добавление тестовых библиотек в целевые приложения.

Еще раз откройте build.gradle.kts в общем модуле. Внутри sourceSetsблока обязательно добавьте эти элементы:

//1 val iosX64Test by getting val iosArm64Test by getting val iosSimulatorArm64Test by getting val iosTest by creating { dependsOn(commonTest) iosX64Test.dependsOn(this) iosArm64Test.dependsOn(this) iosSimulatorArm64Test.dependsOn(this) } //2 val androidTest by getting { dependencies { implementation(kotlin("test-junit")) implementation("junit:junit:4.13.2") } } //3 val desktopTest by getting { dependencies { implementation(kotlin("test-junit")) implementation("junit:junit:4.13.2") } }

  1. Вы создаете исходный набор для платформы iOS с именем iosTest, комбинируя различные архитектуры платформы. iOS не нуждается в каких-либо конкретных зависимостях для тестирования. Необходимые библиотеки уже есть в системе.
  2. Для Android вы добавляете исходный набор с зависимостями в junit. Это обеспечит конкретную реализацию предоставленных аннотаций kotlin.testбиблиотекой.
  3. Поскольку рабочий стол использует JVM, как и Android, вы добавляете тот же набор зависимостей, что и Android.

Выполните синхронизацию Gradle, чтобы загрузить зависимости. Теперь снова запустите тест для Android. Это пройдет, и система не выдаст никаких ошибок.

Написание тестов для RemindersViewModel

Когда все зависимости для модульного тестирования готовы, пришло время создать несколько полезных тестовых функций.

Вернитесь к RemindersViewModelTest.kt . Поскольку вы тестируете viewModel, вам нужен экземпляр RemindersViewModelпод рукой. Добавьте lateinitсвойство в класс по этому поводу следующим образом:

private lateinit var viewModel: RemindersViewModel

Далее нужно как-то инициализировать это свойство. При написании тестов вы можете пометить функцию @BeforeTestаннотацией. Это гарантирует, что конкретная функция запускается перед каждым тестом в классе. Кажется, это хорошее место для настройки viewModel. Добавьте эту функцию в класс:

@BeforeTest fun setup() { viewModel = RemindersViewModel() }

Примечание . Также есть @AfterTestаннотация. Как следует из названия, он запускается после каждого теста в классе. Вы можете использовать функции, помеченные этой аннотацией, для выполнения необходимой очистки.

Что касается тела testCreatingReminder(), обновите его с помощью:

@Test fun testCreatingReminder() { //1 val title = "New Title" //2 viewModel.createReminder(title) //3 val count = viewModel.reminders.count { it.title == title } //4 assertTrue( actual = count == 1, message = "Reminder with title: $title wasn't created.", ) }

  1. Во-первых, вы создаете константу заголовка.
  2. Вы используете createReminderметод viewModel для создания нового напоминания.
  3. Затем вы проверяете количество элементов в remindersсвойстве viewModel с названием, которое вы использовали. Если вы столкнулись с ошибкой, касающейся видимости reminders, не беспокойтесь. Ты скоро это исправишь.
  4. kotlin.testбиблиотека включает в себя несколько функций утверждения , которыми вы можете воспользоваться. Здесь вы используете, assertTrueчтобы проверить, countравно ли 1. Если это правда, это означает, что процесс создания прошел успешно. Если нет, вы показываете сообщение в консоли.

Свойство remindersв RemindersViewModel было private, когда вы его написали. Поскольку commonTest находится в том же модуле, что и commonMain , вы можете изменить модификатор видимости для этого свойства на internal. Таким образом, посторонние, использующие общий модуль, такой как androidApp и iosApp , не увидят никаких изменений, а свойство будет видно вашим тестовым функциям.

Откройте RemindersViewModel.kt и измените вышеупомянутое свойство на это:

internal val reminders: List<Reminder> get() = repository.reminders

Теперь пришло время запустить тест. Чтобы запустить тесты на всех платформах одновременно, вы можете попробовать одно из следующих действий:

  • Выберите allTests из списка задач на панели Gradle в Android Studio.
Рис. 8.6 — Выбор всех тестов из панели Gradle

Рис. 8.6 — Выбор всех тестов из панели Gradle

  • Запустите команду ./gradlew :shared:allTestsв Терминале, пока вы находитесь в рабочем каталоге проекта.

Какой бы вариант вы ни выбрали, у вас будет успешный тест для всех платформ. Ура!

Рис. 8.7 – Успешная проверка создания напоминания

Рис. 8.7 – Успешная проверка создания напоминания

Написание тестов для платформы

Все детали реализации класса RemindersViewModel находились в исходном наборе commonMain . Однако класс Platform немного отличается. Как вы помните, Platform использует механизм ожидаемого/фактического. Это означает, что реализация отличается на каждой платформе и дает разные результаты.

Чтобы решить эту проблему, вам нужно иметь несколько наборов тестов. Они будут следовать шаблону исходных наборов, который вы видели на предыдущих шагах. Создайте каталоги androidTest , iosTest и desktopTest в общем модуле. Не забудьте добавить каталоги com/raywenderlich/organize .

У вас есть два варианта: либо вы используете один и тот же механизм ожидаемого/фактического для своего тестового класса, либо вы создаете тестовые классы независимо друг от друга в каждом исходном наборе. В обоих методах система будет запускать все функции, аннотированные с помощью @Test. Однако, поскольку ожидаемое/фактическое заставит вас выполнять ожидаемые тестовые функции, это более безопасный выбор с точки зрения структуры.

В папке commonTest создайте класс с именем PlatformTestв пакете com.raywenderlich.organize и определите класс следующим образом:

expect class PlatformTest { @Test fun testOperatingSystemName() }

Здесь вы обещаете реализовать тестовую функцию с именем testOperatingSystemName. Вы можете добавить любую тестовую функцию, но для краткости это единственная тестовая функция платформы, которую вы увидите в этой главе.

Вы много слышали о том, как создавать настоящие классы. Если вы еще недостаточно освоились с процессом, вернитесь и просмотрите главу 6.

Андроид

Создайте PlatformTest.kt внутри каталогов, созданных ранее в androidTest, и обновите его следующим образом:

actual class PlatformTest { private val platform = Platform() @Test actual fun testOperatingSystemName() { assertEquals( expected = "Android", actual = platform.osName, message = "The OS name should be Android." ) } }

Довольно просто, не так ли? Вы утверждаете, что название операционной системы должно быть «Android».

iOS

actual class PlatformTest { private val platform = Platform() @Test actual fun testOperatingSystemName() { assertTrue( actual = platform.osName.equals("iOS", ignoreCase = true) || platform.osName == "iPadOS", message = "The OS name should either be iOS or iPadOS." ) } }

Вы проверяете, является ли имя ОС iOS или iPadOS .

Рабочий стол

actual class PlatformTest { private val platform = Platform() @Test actual fun testOperatingSystemName() { assertTrue( actual = platform.osName.contains("Mac", ignoreCase = true) || platform.osName.contains("Windows", ignoreCase = true) || platform.osName.contains("Linux", ignoreCase = true) || platform.osName == "Desktop", message = "Non-supported operating system" ) } }

Это немного сложно проверить должным образом. На данный момент вы можете проверить, содержит ли указанное имя ОС поддерживаемые приложения платформы. Если нет, пусть тест провалится.

Если вы запустите задачу allTests Gradle, как и раньше, система также запустит эти тесты. Попробуйте, чтобы увидеть новую партию успешных тестов.

тесты пользовательского интерфейса

До сих пор подход, который вы использовали в этой книге, заключался в совместном использовании бизнес-логики в общем модуле с помощью Kotlin Multiplatform и создании пользовательского интерфейса на каждой платформе с использованием доступного собственного набора инструментов. Следовательно, вы также можете делиться тестами бизнес-логики внутри общего модуля.

Для тестирования пользовательского интерфейса вы можете с уверенностью предположить, что KMP отсутствует. Вы тестируете пользовательские интерфейсы Android и настольных компьютеров с помощью Jetpack Compose Tests , а пользовательский интерфейс iOS — с помощью XCUITest .

Андроид

Вы создали пользовательский интерфейс для Organize полностью с помощью Jetpack Compose. Тестирование макетов Compose отличается от тестирования пользовательского интерфейса на основе View. Инструментарий пользовательского интерфейса на основе представления определяет, какие свойства имеет представление, например прямоугольник, который оно занимает, его свойства и т. д. В Compose некоторые составные элементы могут создавать пользовательский интерфейс в иерархии. Следовательно, вам нужен новый механизм сопоставления элементов пользовательского интерфейса.

К счастью, создатели Jetpack Compose учли это и предоставили необходимые инструменты для тестирования макетов.

Откройте build.gradle.kts в androidApp и добавьте их внутрь dependenciesблока:

androidTestImplementation( "androidx.compose.ui:ui-test-junit4:${rootProject.extra["composeVersion"]}" ) debugImplementation( "androidx.compose.ui:ui-test-manifest:${rootProject.extra["composeVersion"]}" ) androidTestImplementation("androidx.fragment:fragment-testing:1.4.0") androidTestImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test:runner:1.4.0")

В defaultConfigразделе androidблока добавьте это, чтобы сообщить системе, как запускать тесты:

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

Идите вперед и синхронизируйте свой проект сейчас. Далее пришло время создать пакеты и файлы. Внутри каталога src создайте следующие вложенные папки: androidTest/java/com/raywenderlich/organize/android/

Вы собираетесь воспроизвести ту же структуру пакета основного каталога.

Затем создайте файл Kotlin с именем AppUITest.kt и определите в нем класс с таким же именем.

class AppUITest { @get:Rule val composeTestRule = createAndroidComposeRule<MainActivity>() }

Добавьте свойство типа AndroidComposeTestRule. Создает createAndroidComposeRuleтестовое правило для действия, которое вы предоставляете. Он вызывает активность, чтобы вы могли запускать свои тесты.

Самый первый тест, который вы напишете, — это проверка наличия кнопки « О программе». Как вы помните, кнопка находится в правом верхнем углу приложения и имеет значок i . Вы можете сопоставить этот элемент в своих тестах с помощью механизма, называемого семантикой .

Семантика

Семантика придает смысл части пользовательского интерфейса — будь то простая кнопка или целый набор составных элементов. Структура семантики в первую очередь предназначена для обеспечения доступности. Однако тесты могут использовать информацию, предоставляемую семантикой об иерархии пользовательского интерфейса.

Вы прикрепляете семантику к компонуемым через Modifier .

Откройте RemindersView.kt в модуле androidApp и прикрепите semanticsмодификатор к IconButtonфайлу Toolbarcompasable. Это IconButtonбудет выглядеть так:

IconButton( onClick = onAboutButtonClick, modifier = Modifier.semantics { contentDescription = "aboutButton" }, ) { Icon( imageVector = Icons.Outlined.Info, contentDescription = "About Device Button", ) }

Вы прикрепляете семантику к IconButtonописанию содержимого aboutButton .

Вернитесь к AppUITest.kt и выполните тестовую функцию, которую вы собирались написать:

@Test fun testAboutButtonExistence() { composeTestRule .onNodeWithContentDescription("aboutButton") .assertIsDisplayed() }

Используя composeTestRuleзаданное вами, вы запрашиваете узел с установленным вами описанием содержимого и утверждаете, отображается ли оно.

Запустите тест, используя кнопку запуска в поле кода. Вы увидите, что эмулятор или подключенное устройство запускает ваше приложение для экземпляра, а затем закрывает его. Если все пройдет хорошо, вы должны увидеть отчет о пройденном тесте.

Рис. 8.8 — Успешная проверка наличия кнопки

Рис. 8.8 — Успешная проверка наличия кнопки

Далее нужно проверить, открывается ли страница « О программе» и закрывается ли она успешно. Добавьте следующую функцию, чтобы проверить это:

@Test fun testOpeningAndClosingAboutPage() { //1 composeTestRule .onNodeWithContentDescription("aboutButton") .performClick() //2 composeTestRule .onNodeWithText("About Device") .assertIsDisplayed() //3 composeTestRule .onNodeWithContentDescription("Up Button") .performClick() //4 composeTestRule .onNodeWithText("Reminders") .assertIsDisplayed() }

  1. Вы находите кнопку « О программе» , используя определенную вами семантику, и имитируете нажатие на нее.
  2. Проверьте, есть ли на экране текст с содержимым « Об устройстве» . Страница « О программе» имеет этот заголовок, и он присутствует только в том случае, если эта страница отображается на экране. Однако это не лучший способ сделать это. Этот тест не пройдет, если вы локализуете свое приложение на другой язык. Использование семантики всегда является лучшим выбором.
  3. Если бы вы установили описание содержимого для кнопок, как вы это сделали с кнопкой «Вверх» на панели инструментов, вы можете использовать это без настройки и запроса семантики. Вы находите кнопку и выполняете щелчок по ней.
  4. Когда вы закроете страницу « О программе», приложение должно оказаться на странице « Напоминания ». Проверьте заголовок страницы, если это так.

Запустите тест, и он пройдет.

Рабочий стол

Поскольку код пользовательского интерфейса для Android и рабочего стола практически одинаков, тесты будут очень похожими. Хотя установка немного другая. Код уже есть в стартовом проекте. Вот различия, которые вы должны учитывать:

  • Тестовые зависимости бывают разные. Кроме того, библиотека Desktop Compose для использования JUnit с тестированием пользовательского интерфейса находится в экспериментальном режиме, и вы должны дать согласие на ее использование. Взгляните на build.gradle.kts в модуле desktopApp .

named("jvmTest") { dependencies { @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) implementation(compose.uiTestJUnit4) implementation(compose.desktop.currentOs) } }

  • Многих функций утверждений еще нет. Например, assertIsDisplayed()не имеет реализации на рабочем столе. assertExists()Вместо этого вы можете использовать .
  • Поскольку на рабочем столе нет Activity для размещения ваших тестов, вам нужно заложить основу самостоятельно. Откройте AppUITest.kt в модуле desktopApp . По сути, есть два основных отличия: Во- первых, вы должны создавать createComposeRuleпо-другому и использовать более общий createComposeRule()вызов. Во-вторых, есть setUpметод, который будет запускаться перед всеми вашими тестами. Здесь вы создаете компоновку, аналогичную запускаемому вами приложению. Главное отличие в том, что здесь нет окон.

@Before fun setUp() { composeTestRule.setContent { var screenState by remember { mutableStateOf(Screen.Reminders) } when (screenState) { Screen.Reminders -> RemindersView( onAboutButtonClick = { screenState = Screen.AboutDevice } ) Screen.AboutDevice -> AboutView() } } }

  • Поскольку в этом наборе тестов нет окон, вторая тестовая функция будет такой:

@Test fun testOpeningAboutPage() { //1 composeTestRule .onNodeWithText("Reminders") .assertExists() //2 composeTestRule .onNodeWithContentDescription("aboutButton") .performClick() //3 composeTestRule.waitForIdle() //4 composeTestRule .onNodeWithContentDescription("aboutView") .assertExists() }

  1. Во-первых, вы проверяете, находитесь ли вы на странице « Напоминания », подтверждая существование заголовка «Напоминания».
  2. Вы имитируете щелчок по кнопке « О программе» .
  3. Далее дождитесь окончания перекомпоновки. Когда тестовое правило Compose было действием, оно делало это автоматически. Теперь ваша работа — заставить ваши тесты ждать.
  4. Наконец, проверьте, существует ли в иерархии элемент с семантикой aboutView .

Запустите набор тестов, чтобы убедиться, что все они пройдены.

iOS

Чтобы сделать код пользовательского интерфейса доступным для тестирования в Xcode, вам необходимо добавить в проект цель тестирования пользовательского интерфейса. Пока проект приложения iOS открыт в Xcode, выберите « Файл» ▸ «Создать» ▸ «Цель… » в строке меню.

Пролистайте вниз, пока не найдете UI Testing Bundle .

Рис. 8.9 — Шаблон новой цели Xcode

Рис. 8.9 — Шаблон новой цели Xcode

Нажмите «Далее» . Значения по умолчанию для целевого имени и других параметров обычно подходят. Нажмите « Готово» , чтобы позволить Xcode создать для вас цель теста пользовательского интерфейса.

Посмотрите в файловом навигаторе. Xcode создал для вас папку с двумя тестовыми классами.

Рис. 8.10 — Целевые файлы Xcode UI Test Target

Рис. 8.10 — Целевые файлы Xcode UI Test Target

Вы можете безопасно удалить iosAppUITestsLaunchTests.swift . Откройте iosAppUITests.swift и удалите все содержимое класса. Вы собираетесь написать пару тестовых функций здесь.

Во-первых, сохраните экземпляр приложения как свойство в тестовом классе.

private let app = XCUIApplication()

Во-вторых, переопределить setUpфункцию. Система вызывает этот метод перед запуском каждого теста. Это похоже на то, когда вы помечаете тестовую функцию в Kotlin с помощью @BeforeTest.

override func setUp() { continueAfterFailure = false app.launch() }

Эта функция предотвратит продолжение тестов в случае возникновения ошибок, а затем запустит приложение, чтобы вы могли запустить свои тесты.

Далее напишите тест, чтобы проверить наличие кнопки « О программе».

func testAboutButtonExistence() { XCTAssert(app.buttons["About"].exists) }

Функции утверждения в тестовых средах Xcode обычно начинаются с XCTAssert. Это самое простое, что вы можете использовать, и ему нужен логический параметр. Запросите все кнопки приложения и найдите одну с заголовком « О программе».

Примечание . Тестовые функции Xcode должны начинаться с test..., иначе Xcode не идентифицирует их как тестовые функции и, следовательно, не запустит их.

Как и в Android Studio, вы можете запускать тесты с помощью кнопки в поле кода. Вы также можете выбрать кнопку « Тест» в меню « Продукт » или нажать Command-U .

Вы можете немного улучшить этот код. Представьте, что вы локализовали свое приложение на французский язык. Когда вы запускаете приведенный выше тест на французском языке, заголовок кнопки не будет « О программе», поэтому тест завершится ошибкой. Вы можете легко решить это.

Перейдите в ContentView.swift и прикрепите предстоящий модификатор к Buttonэлементу:

.accessibilityIdentifier("aboutButton")

Теперь Buttonэлемент будет следующим:

Button { shouldOpenAbout = true } label: { Label("About", systemImage: "info.circle") .labelStyle(.titleAndIcon) } .accessibilityIdentifier("aboutButton") .padding(8) .popover(isPresented: $shouldOpenAbout) { AboutView() .frame( idealWidth: 350, idealHeight: 450 ) }

Отныне вы можете ссылаться на эту конкретную кнопку, используя aboutButtonнезависимо от ее названия.

Затем вы можете изменить тело теста на это:

XCTAssert(app.buttons["aboutButton"].exists)

Это похоже на semanticsмодификатор в Jetpack Compose. Запустите тест еще раз, чтобы убедиться, что ничего не изменилось в поведении и результате.

Запись тестов пользовательского интерфейса

В Xcode есть классная функция, которой вы можете воспользоваться, чтобы упростить процесс создания тестов пользовательского интерфейса.

Создайте новую тестовую функцию и поместите курсор в пустое тело.

func testOpeningAndClosingAboutPage() { // Put the cursor here }

Внизу страницы появится кнопка « Запись ». Нажмите здесь. Приложение будет работать на симуляторе, и Xcode превратит любое действие, которое вы выполняете в приложении, в код.

Рис. 8.11 — Кнопка Xcode UI Test Record

Рис. 8.11 — Кнопка Xcode UI Test Record

Выполните эти действия по порядку:

  1. Нажмите кнопку « О программе ».
  2. Когда появится страница « О программе», нажмите кнопку « Готово » .
  3. Остановите запись с помощью той же кнопки, с помощью которой вы начали запись.

Обратите внимание на тестовую функцию. Xcode добавил код для вас. Это будет что-то вроде этого.

func testOpeningAndClosingAboutPage() { let app = XCUIApplication() app.toolbars["Toolbar"].buttons["aboutButton"].tap() app.navigationBars["About Device"].buttons["Done"].tap() }

Если это все, что вы имели в виду, то все готово! В противном случае это даст вам отправную точку для написания тестов. Вы также можете узнать из этой функции, как находить элементы на экране и действовать с ними.

Еще одна вещь, на которую следует обратить внимание, это то, что Xcode автоматически переходит к , accessibilityIdentifierесли вы его установили. Если нет, он использует статический заголовок для запроса элементов. Всегда рекомендуется устанавливать этот модификатор для элементов.

Тем не менее, вы можете воспользоваться подсказками из автоматической системы записи тестов Xcode и использовать эту тестовую функцию:

func testOpeningAndClosingAboutPage() { //1 app.buttons["aboutButton"].tap() //2 let aboutPageTitle = app.staticTexts["About Device"] XCTAssertTrue(aboutPageTitle.exists) //3 app.navigationBars["About Device"].buttons["Done"].tap() //4 let remindersPageTitle = app.staticTexts["Reminders"] XCTAssertTrue(remindersPageTitle.exists) }

  1. Имитация нажатия кнопки « О программе» при запуске приложения.
  2. Проверьте, есть ли на экране текст с содержимым « Об устройстве» . Страница « О программе» имеет этот заголовок, и он присутствует только в том случае, если эта страница отображается на экране.
  3. Найдите кнопку « Готово » на одной из панелей навигации приложения с заголовком « Об устройстве » и попробуйте нажать на нее.
  4. Когда вы закроете страницу « О программе», приложение должно оказаться на странице « Напоминания ». Проверьте заголовок страницы, если это так.

Запустите все тесты в классе iosAppUITests , поместив курсор в середину его имени и нажав Command-U .

Просмотрите результаты в консоли Xcode или посмотрите на зеленые галочки в поле кода и в навигаторе тестов и радуйтесь!

Рис. 8.12 — Успех теста пользовательского интерфейса Xcode — консоль

Рис. 8.12 — Успех теста пользовательского интерфейса Xcode — консоль

Рис. 8.13 — Успех теста пользовательского интерфейса Xcode — Gutter

Рис. 8.13 — Успех теста пользовательского интерфейса Xcode — Gutter

Испытание

Вот вызов для вас, чтобы увидеть, есть ли у вас идея. Решение содержится в материалах к этой главе.

Задача: Написание тестов для RemindersRepository

Углубляясь на один уровень в архитектурный памятник приложения, очень важно иметь пуленепробиваемый репозиторий. В конце концов, репозитории — это основа ViewModels в Organize . Хотя на данный момент это может показаться легким и похожим на модели представлений, вы увидите, насколько эти тесты будут играть жизненно важную роль при подключении базы данных к репозиторию по мере продвижения вперед.

Имея в виду это объяснение, попробуйте создать набор тестов для RemindersRepository.

Ключевые моменты

  • KMP поможет вам написать меньше тестового кода так же, как он помог вам написать меньше кода бизнес-логики.
  • Вы можете писать тесты для своего общего кода, а также для кода для конкретной платформы — и все это на Kotlin.
  • Объявление зависимостей от тестовой библиотеки для каждой платформы необходимо. KMP запустит ваши тесты в предоставленной среде, например JUnit на JVM.
  • Возможно использование механизмов ожидаемого/фактического в тестовых кодах.
  • Для тестов пользовательского интерфейса вы можете обратиться к предоставленному решению для каждой платформы: Jetpack Compose Tests для пользовательских интерфейсов, созданных с помощью Jetpack Compose, и XCUITest для пользовательских интерфейсов, созданных с помощью UIKit или SwiftUI.

Куда пойти отсюда?

В этой главе мы едва касались поверхности тестирования. Он не говорил о моках и заглушках и старался не использовать сторонние библиотеки, если уж на то пошло. Однако стоит упомянуть несколько библиотек:

  • Kotest : мультиплатформенная библиотека тестирования Kotlin с расширенными утверждениями и поддержкой тестирования свойств. Он может генерировать значения для крайних случаев и случайных значений.
  • Turbine : небольшая мультиплатформенная библиотека, предназначенная для тестирования Koltin Flows. В этой главе не говорилось о сопрограммах и потоках. Однако, если вы когда-нибудь хотели протестировать их, взгляните на Turbine, посколькуkotlinx-coroutines-testбиблиотека пока не поддерживает Kotlin/Native.
  • MockK : Это самая известная библиотека для мокинга в Котлине. Хотя доступна мультиплатформенная версия, в ней отсутствует поддержка iOS.

Если вы хотите узнать больше о тестировании в целом, есть отличные ресурсы, такие как скринкасты и статьи, а также эти две книги