Рассмотрим следующую структуру
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, который проверяет, является ли второй элемент вкладки
Ценим ваш вклад!
В рамках автоматизированного теста для студенческих упражнений





То, что вы пытаетесь протестировать/подтвердить, — это 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")
}
Почему это вместо проверки моментального снимка? Снимки — это хороший способ подтвердить, что внешний вид соответствует ранее утвержденному изображению. Но чтобы снэпшот-тесты были достаточно быстрыми для рефакторинга, они должны совпадать попиксельно. Изображения не будут совпадать, если:
Эти различия обычно незначительны. Существует способ выполнить нечеткое сопоставление со снимками, чтобы игнорировать небольшие различия. Но это делает тесты моментальных снимков намного медленнее.
Причина, по которой мне нужны тесты, состоит в том, чтобы сделать рефакторинг небольшими проверенными шагами. Для этого тесты должны быть максимально быстрыми. При самом быстром выполнении попиксельного сопоставления снэпшот-тесты на порядок медленнее, чем обычные модульные тесты. Обычно их можно использовать для рефакторинга на такой скорости. Но нечеткое сопоставление замедляет их еще на порядок.
UITests настолько медленные, что о них не может быть и речи. Они также не позволяют вам заменять подделки, чтобы изолировать части вашей системы для облегчения тестирования.
ViewInspector отвечает моим требованиям: тесты быстрые и стабильные.
Почему вы хотите проверить это?