> Как реализовать unit test для функции getValue (iOS, Swift)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: Revolut

Стек: iOS, Swift

> Пример ответа

Короткий ответ

Unit test для функции getValue пишется через XCTest: создаём тестовый класс, вызываем функцию с известными входными данными и сравниваем результат с ожидаемым через XCTAssertEqual. Важно покрыть основные сценарии: успешный возврат значения, nil, граничные случаи и ошибки. Тест должен быть изолированным, быстрым и не зависеть от внешних состояний. Для функций с зависимостями используем protocol-based mocking или dependency injection.

Подробное объяснение

Unit test проверяет поведение функции в изоляции. Для getValue нужно определить:

  • Контракт функции: что она принимает, что возвращает, какие побочные эффекты.
  • Сценарии: happy path, edge cases, error handling.
  • Зависимости: если функция обращается к сети, БД или UserDefaults - их нужно замокать.

В Swift тесты строятся на XCTest. Каждый тест - метод, начинающийся с test. Используем XCTAssert* для проверок. Для асинхронных функций - XCTestExpectation. Для тестирования ошибок - XCTAssertThrowsError.

Структура теста: Arrange (подготовка), Act (вызов), Assert (проверка). Это делает тест читаемым и поддерживаемым.

На практике

Для senior-позиции важно показать не просто написание теста, а понимание дизайна тестируемого кода. Если getValue - метод класса с зависимостями, нужно продемонстрировать:

  • Использование протоколов для абстракции зависимостей.
  • Внедрение моков через инициализатор или property.
  • Тестирование через given/when/then структуру.
  • Покрытие edge cases: пустая строка, отрицательные числа, большие значения, nil.

Также стоит упомянуть измерение покрытия кода (code coverage) и важность тестирования не только результата, но и побочных эффектов.

Пример кода

SWIFT
import XCTest
// Тестируемая функция
class DataProvider {
func getValue(for key: String, from storage: Storage) -> Int? {
guard let value = storage.value(forKey: key) as? Int else {
return nil
}
return value > 0 ? value : nil
}
}
// Протокол для мока
protocol Storage {
func value(forKey key: String) -> Any?
}
class MockStorage: Storage {
var storedValue: Any?
func value(forKey key: String) -> Any? {
return storedValue
}
}
class DataProviderTests: XCTestCase {
var provider: DataProvider!
var mockStorage: MockStorage!
override func setUp() {
super.setUp()
provider = DataProvider()
mockStorage = MockStorage()
}
override func tearDown() {
provider = nil
mockStorage = nil
super.tearDown()
}
func testGetValue_WithPositiveNumber_ReturnsValue() {
// given
mockStorage.storedValue = 42
// when
let result = provider.getValue(for: "key", from: mockStorage)
// then
XCTAssertEqual(result, 42)
}
func testGetValue_WithNonPositiveNumber_ReturnsNil() {
// given
mockStorage.storedValue = -5
// when
let result = provider.getValue(for: "key", from: mockStorage)
// then
XCTAssertNil(result)
}
func testGetValue_WithNonIntValue_ReturnsNil() {
// given
mockStorage.storedValue = "not an int"
// when
let result = provider.getValue(for: "key", from: mockStorage)
// then
XCTAssertNil(result)
}
func testGetValue_WithNilValue_ReturnsNil() {
// given
mockStorage.storedValue = nil
// when
let result = provider.getValue(for: "key", from: mockStorage)
// then
XCTAssertNil(result)
}
}

Как отвечать на собеседовании

Начни с краткого описания подхода: XCTest, структура Arrange-Act-Assert. Затем объясни, как обрабатываешь зависимости - через протоколы и моки. Покажи пример кода, но не углубляйся в детали реализации, если не просят. Подчеркни важность тестирования edge cases и изоляции. Если функция асинхронная - упомяни использование XCTestExpectation. Для senior важно показать, что ты думаешь о тестируемости кода на этапе проектирования, а не только пишешь тесты постфактум.

Что проверяет интервьюер

Интервьюер оценивает:

  • Понимание основ unit-тестирования: изоляция, детерминизм, скорость.
  • Умение работать с зависимостями: моки, стабы, DI.
  • Внимание к edge cases и граничным условиям.
  • Способность структурировать тесты: читаемость, именование, организация.
  • Знание XCTest API: XCTAssert*, setUp/tearDown, XCTestExpectation.
  • Понимание разницы между unit, integration и UI-тестами.

Типичные ошибки

  • Тест зависит от реальных внешних ресурсов: сети, файловой системы, времени.
  • Использование реальных объектов вместо моков - тест становится недетерминированным.
  • Проверка только happy path, без nil и ошибок.
  • Слишком много проверок в одном тесте - сложно понять причину падения.
  • Неправильное именование тестов - неясно, что именно проверяется.
  • Игнорирование setUp и tearDown - утечки состояния между тестами.
  • Тестирование реализации, а не поведения - тест ломается при рефакторинге.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью