Подтвердить что ты не робот

Издевательская в Swift

Как вы издеваетесь над объектом в Swift?

Протокол Mirror звучал многообещающе, но сейчас он мало что делает.

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

Любые другие идеи?

Почему не OCMock?

Из источника:

Могу ли я использовать OCMock, используя функциональность языкового моста?

Да, но с ограничениями. Если вы храбры. На данный момент это очень экспериментальный. Там нет гарантии, что OCMock будет когда-либо полностью поддерживать Swift.

Известные ограничения:

  • Тесты должны быть записаны в Objective-C
  • Объекты, которые следует издеваться, должны наследовать от NSObject
  • Отсутствие stubbing/expecting/verify методов класса
4b9b3361

Ответ 1

NSHipster затрагивает языковые возможности в Swift, которые делают ненужную внешнюю насмешливую библиотеку:

В Swift классы могут быть объявлены в определении функции, позволяя макетным объектам быть чрезвычайно автономными. Просто объявляйте ложные методы внутреннего класса, переопределения и [sic]:

func testFetchRequestWithMockedManagedObjectContext() {
    class MockNSManagedObjectContext: NSManagedObjectContext {
        override func executeFetchRequest(request: NSFetchRequest!, error: AutoreleasingUnsafePointer<NSError?>) -> [AnyObject]! {
            return [["name": "Johnny Appleseed", "email": "[email protected]"]]
        }
    }

    ...
}

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

Единственное, что библиотека, такая как OCMock, обеспечивает очень полезную, это ее методы проверки, чтобы гарантировать, что были вызваны макеты. Это можно добавить вручную, но автоматическое добавление приятно.

Ответ 2

Я создаю свои макеты, обертывая все в протокол. Я использую класс макета для соответствия рассматриваемому протоколу, например:

protocol Dog: class {
    var name: String { get }

    func bark()
}

class DogImpl: Dog {
    var name: String

    init(name: String) {
        self.name = name
    }

    func bark() {
        print("Bark!")
    }
}

class DogMock: Dog {
    var name = "Mock Dog"
    var didBark = false

    func bark() {
        didBark = true
    }
}

Я использую это в сочетании с инъекцией зависимостей для достижения полного охвата тестированием. Это много шаблонов, но до сих пор у меня не было никаких проблем с этим подходом.

Что касается подкласса, насмехающегося, вы столкнетесь с проблемами с классами final или если у них нетривиальные инициализаторы.

Ответ 3

Я хочу указать что-то в дополнение к заметному ответу - я не знаю, это ошибка или нет.

Если вы каким-то образом подклассифицируете NSObject (в моем случае я был подклассифицирован UIView, который является внутренним подклассом NSObject), вам нужно объявить переопределенную функцию с помощью @objc, иначе ваш тест не будет компилироваться. В моем случае сам компилятор сбой:

Ошибка сегментации: 11

Итак, следующий класс:

public class ClassA: UIView
{
    @objc public func johnAppleseed() {

    }
}

Следует тестировать устройство следующим образом:

class ClassATests: XCTestCase {

    func testExample()
    {
        class ClassAChildren: ClassA
        {
            @objc private override func johnAppleseed() {

            }
        }
    }
}

Ответ 4

Вы можете достичь такого рода насмешек MockFive.

Суть его в том, что вам нужно создать макет "вручную" исходного класса, который вы хотите высмеять -

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

Я написал статью о том, как ее использовать здесь.

Ответ 5

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

Примеры классов:

class ExampleObject {

    var number: Int = 0

    func evaluate(number: Int) -> Bool {
        return self.number == number
    }

}

class ExampleChecker {

    func check(object: ExampleObject) -> Bool {
        return object.evaluate(5)
    }

}

Пример теста:

@testable import App
import Cuckoo
import XCTest

class ExampleCheckerTests: XCTestCase {

    func testCheck() {
        // 1. Arrange
        let object = MockExampleObject().spy(on: ExampleObject())
        stub(object) { object in
            when(object.evaluate(any())).thenDoNothing()
        }
        let checker = ExampleChecker()

        // 2. Action
        checker.check(object)

        // 3. Assert
        _ = verify(object).number.get
        verify(object).evaluate(any())
        verifyNoMoreInteractions(object)
    }

}

Ответ 6

Из-за ограничений, которые вы написали, OCMock не очень хорошо работает в Swift (так как каждая фреймворк сильно зависит от времени выполнения).

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

https://github.com/MakeAWishFoundation/SwiftyMocky

Я не буду вдаваться в подробности, у него есть свои незначительные ограничения, но из того, что я вижу, он обладает самым широким набором функций из фреймворков Swift (по крайней мере, из тех, которые я знаю), включая поддержку обобщенных элементов, членов @objc и обновление макетов при Вы пишете/меняете протоколы (режим наблюдателя).

Ответ 7

Может быть, кто-то найдет это полезным - я использую простые помощники внутри своих проектов для макетирования и проверки зависимостей на основе протокола (очень помогает в архитектурах, подобных VIPER): https://github.com/dratkevich/FakeEquatable