Как протестировать приватные методы класса в Swift?

Ответ

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

1. @testable import (для internal) Доступно только для модулей с включенным тестированием (Enable Testability = YES).

@testable import MyModule

func testInternalMethod() {
    let sut = MyClass()
    // Доступны только internal-члены, не private/fileprivate
}

2. Fileprivate-расширение в том же файле

// В исходном файле MyClass.swift
class MyClass {
    fileprivate func privateMethod() -> Int { return 42 }
}

// Расширение для тестов в том же файле
#if DEBUG
extension MyClass {
    func exposedForTesting() -> Int {
        return privateMethod()
    }
}
#endif

3. Рефлексия (не рекомендуется) Хрупкий подход, ломается при изменении сигнатур.

let mirror = Mirror(reflecting: object)
// Сложный и ненадежный доступ к приватным свойствам

Рекомендация: Проектируйте классы так, чтобы приватная логика вызывалась через публичные методы, которые легко тестировать. Если метод сложно протестировать — возможно, он должен быть вынесен в отдельный тестируемый компонент.

Ответ 18+ 🔞

А, ну это классика, блядь! Опять эти приватные методы, как заноза в жопе. Слушай, давай по-честному: тестировать их напрямую — это пиздец какой антипаттерн, ёпта. Это как пытаться проверить, что у человека в желудке, через жопу — технически можно, но неебически неудобно и против всех правил приличия. Ты же инкапсуляцию ломаешь нахуй!

Но если прям приперло, как говно к стенке, и по-другому никак, есть несколько кривых тропинок. Только смотри, не обосрись на них.

Первый способ, самый цивилизованный — @testable import.
Это как если бы ты был родственником, которому разрешили зайти в квартиру, но в спальню — ни ногой. Работает только для internal методов, private ему всё равно как слону дробина.

@testable import MyModule // Включи в настройках таргета Enable Testability, а то нихуя не выйдет

func testInternalMethod() {
    let sut = MyClass()
    // Тут тебе доступно всё internal-наследие, но приватное — хуй тебе, а не доступ.
}

Второй способ — хитрость для fileprivate.
Ты в том же файле, где твой класс, делаешь расширение специально для тестов. Как потайная дверь для своих.

// Это в MyClass.swift, блядь
class MyClass {
    fileprivate func secretSauce() -> Int { return 666 }
}

// А это — наш чит-код, только для DEBUG
#if DEBUG
extension MyClass {
    func exposeTheSauceForTesting() -> Int {
        return secretSauce() // Вот и добрались, сука!
    }
}
#endif

Третий способ — рефлексия, она же зеркало.
Это уже полный пиздец и шаманство, ядрёна вошь. Сломается от чиха, если сигнатуру метода поменяют. Бери на вооружение, только если готов каждый день переписывать тесты.

let mirror = Mirror(reflecting: object)
// Дальше начинается ад: ковыряешься в children, ищешь нужное свойство...
// Один хрен, private-метод так не выцепишь. Хуйня, короче.

А теперь, блядь, главное, что я хочу донести, чтобы у тебя в башке щёлкнуло. Правильный путь — это спроектировать классы так, чтобы не было этой боли в сраке. Если приватный метод настолько сложный, что его хочется отдельно потестировать — вынеси его, сука, в отдельную сущность! Сделай из него public протокол с реализацией, и тестируй её в открытую. Или убедись, что он вызывается через публичные методы, которые ты и будешь проверять.

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