Издевательская в Swift
Как вы издеваетесь над объектом в Swift?
Протокол Mirror
звучал многообещающе, но сейчас он мало что делает.
Пока единственный подход, который я нашел, - это подкласс и переопределить все методы издевающегося класса. Это, конечно, не настоящий макет, далекий от идеала и много работы.
Любые другие идеи?
Почему не OCMock?
Из источника:
Могу ли я использовать OCMock, используя функциональность языкового моста?
Да, но с ограничениями. Если вы храбры. На данный момент это очень экспериментальный. Там нет гарантии, что OCMock будет когда-либо полностью поддерживать Swift.
Известные ограничения:
- Тесты должны быть записаны в Objective-C
- Объекты, которые следует издеваться, должны наследовать от NSObject
- Отсутствие stubbing/expecting/verify методов класса
Ответы
Ответ 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