Swift XCTest - Как проверить свойства tabItems в TabView?

Рассмотрим следующую структуру

struct MainView: View {
    // The `Model` the   `User` shall be read from
    @EnvironmentObject private var model: Model
    
    var body: some View {
        TabView {
            AccountsOverview()
            .tabItem {
                Image(systemName: "rectangle.stack")
                Text("Accounts")
            }
            TransactionOverview()
            .tabItem {
                Image(systemName: "list.dash")
                Text("Transactions")
            }
        }
    }
}

Как написать XCTest, который проверяет, является ли второй элемент вкладки

  • имеет изображение с именем systemName list.dash
  • имеет текст "Транзакции"

Ценим ваш вклад!

Почему вы хотите проверить это?

LuLuGaGa 03.04.2023 13:05

В рамках автоматизированного теста для студенческих упражнений

Luis G 03.04.2023 17:32
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

То, что вы пытаетесь протестировать/подтвердить, — это View, а для тестирования View следует использовать либо SnapshotTests, либо UITests. Здесь я бы посоветовал начать с SnapshotTests, чтобы проверить ваш компонент представления.

Учитывая, что следующее - это ваше мнение, которое вы хотите проверить

class Model: ObservableObject {}

struct MainView: View {
    @EnvironmentObject private var model: Model

    var body: some View {
        TabView {
            AccountsOverview()
                .tabItem {
                    Image(systemName: "rectangle.stack")
                    Text("Accounts")
                }
            TransactionOverview()
                .tabItem {
                    Image(systemName: "list.dash")
                    Text("Transactions")
                }
        }
    }
}

struct AccountsOverview: View {
    var body: some View {
        Text("AccountsOverview")
    }
}

struct TransactionOverview: View {
    var body: some View {
        Text("TransactionOverview")
    }
}

В настоящее время доступно множество фреймворков для тестирования моментальных снимков, но вы можете использовать библиотеку SnapshotTesting от PointFree. Обратитесь к этой статье от Kodeco, чтобы начать с SnapshotTesting.

Ниже приведен тест моментального снимка для просмотра:

import XCTest
import SnapshotTesting
@testable import <your project target>

class MainViewSnapshotTests: XCTestCase {
    func testExample() throws {
        let view = MainView()
        assertSnapshot(matching: view, as: .image)
    }
}

Ниже приведен эталонный снимок, созданный с использованием вышеуказанного теста моментального снимка.

ViewInspector — это сторонняя библиотека для модульного тестирования SwiftUI. Мы можем найти второй элемент вкладки с

let tabItem = try MainView().inspect().tabView().view(TransactionOverview.self, 1).tabItem()

Это подтверждает, что MainView содержит TabView. Он ищет второго ребенка (то есть с индексом 1) и подтверждает, что это TransactionOverview. Затем он возвращает доступное для проверки представление TabItem.

Затем мы можем найти в этом TabItemText и получить его строку:

let text = try tabItem.find(ViewType.Text.self).string()

Наконец, мы можем проверить, что этот текст соответствует:

XCTAssertEqual(text, "Transactions")

Проверить изображение немного сложнее, потому что нет возможности задать стандартный Image вопрос: «Вы были созданы с использованием системного имени? Что это было?» Сам по себе Image не подлежит исследованию.

Но, как говорится, мы можем решить проблему, введя дополнительный слой. Давайте определим нашу собственную оболочку вокруг Image. Он будет точно таким же, но позволит нам изучить имя системы:

struct ExaminableImage: View {
    let systemName: String

    var body: some View {
        Image(systemName: systemName)
    }
}

Используйте это вместо Image, чтобы сделать его проверяемым. Теперь мы можем написать второй тест. Он начинается так же, как и поиск второго элемента вкладки. Поскольку это дублирующийся код, давайте воспользуемся рефакторингом метода извлечения для создания помощника:

private func secondTabItem() throws -> InspectableView<ViewType.ClassifiedView> {
    try MainView().inspect().tabView().view(TransactionOverview.self, 1).tabItem()
}

Теперь наш новый тест ищет внутри этого второго элемента вкладки ExaminableImage. Мы используем find, как и с тестом Text, потому что нам все равно, где внутри указано изображение. Затем мы преобразуем это просматриваемое представление обратно в реальный вид, используя actualView(). Теперь мы можем запросить его системное имя:

func test_secondTabItem_image() throws {
    let image = try secondTabItem().find(ExaminableImage.self).actualView()

    XCTAssertEqual(image.systemName, "list.dash")
}

Почему это вместо проверки моментального снимка? Снимки — это хороший способ подтвердить, что внешний вид соответствует ранее утвержденному изображению. Но чтобы снэпшот-тесты были достаточно быстрыми для рефакторинга, они должны совпадать попиксельно. Изображения не будут совпадать, если:

  • В машине используется другой чип (особенно если вы используете Apple Silicon, но система сборки работает под управлением Intel).
  • Машина использует другую версию iOS
  • Машина использует другой симулятор (хотя в моментальном снимке может быть указан размер симулятора, фактический симулятор имеет значение)

Эти различия обычно незначительны. Существует способ выполнить нечеткое сопоставление со снимками, чтобы игнорировать небольшие различия. Но это делает тесты моментальных снимков намного медленнее.

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

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

ViewInspector отвечает моим требованиям: тесты быстрые и стабильные.

Другие вопросы по теме