> Как реализовать 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) и важность тестирования не только результата, но и побочных эффектов.
Пример кода
SWIFTimport 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 = nilmockStorage = nilsuper.tearDown()}func testGetValue_WithPositiveNumber_ReturnsValue() {// givenmockStorage.storedValue = 42// whenlet result = provider.getValue(for: "key", from: mockStorage)// thenXCTAssertEqual(result, 42)}func testGetValue_WithNonPositiveNumber_ReturnsNil() {// givenmockStorage.storedValue = -5// whenlet result = provider.getValue(for: "key", from: mockStorage)// thenXCTAssertNil(result)}func testGetValue_WithNonIntValue_ReturnsNil() {// givenmockStorage.storedValue = "not an int"// whenlet result = provider.getValue(for: "key", from: mockStorage)// thenXCTAssertNil(result)}func testGetValue_WithNilValue_ReturnsNil() {// givenmockStorage.storedValue = nil// whenlet result = provider.getValue(for: "key", from: mockStorage)// thenXCTAssertNil(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- утечки состояния между тестами. - Тестирование реализации, а не поведения - тест ломается при рефакторинге.
> Похожие задачи по mobile
Можно ли объединить массивы через reduce
Почему при взятии подмассива массива возвращается ArraySlice, а при взятии подстроки из String возвращается Substring
Какой протокол нужно реализовать для сохранения файла в iOS?
К какому типу типизации относится Swift: динамической или статической?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью