Swiftでオブジェクトをどのようにモックしますか?
Mirror
プロトコルは有望に思えましたが、現時点ではあまり効果がありません。
これまでのところ、私が見つけた唯一のアプローチは、模擬クラスのすべてのメソッドをサブクラス化してオーバーライドすることです。もちろん、これは真のモックではなく、理想とはほど遠い、多くの作業です。
他のアイデアはありますか?
source から:
言語ブリッジ機能を使用してOCMockを使用できますか?
はい、ただし制限があります。あなたが勇敢なら。現在のところ、これは非常に実験的なものです。 OCMockがSwiftを完全にサポートするという保証はありません。
既知の制限:
NSHipster Swift=の言語機能に触れます。これにより、外部のモック作成ライブラリが不要になります。
Swiftでは、クラスを関数の定義内で宣言できるため、モックオブジェクトを非常に自己完結させることができます。モック内部クラスを宣言し、オーバーライドし、必要なメソッドを上書きします:
func testFetchRequestWithMockedManagedObjectContext() {
class MockNSManagedObjectContext: NSManagedObjectContext {
override func executeFetchRequest(request: NSFetchRequest!, error: AutoreleasingUnsafePointer<NSError?>) -> [AnyObject]! {
return [["name": "Johnny Appleseed", "email": "[email protected]"]]
}
}
...
}
ローカルスコープで外部依存関係のサブクラスを作成する機能とXCTestExpectation
の追加により、OCMock
と同じ多くの問題が解決します。
OCMock
などのライブラリが提供する非常に便利なことの1つは、モッククラスが呼び出されたことを確認するための「検証」メソッドです。これを手動で追加することもできますが、自動追加はナイスです。
プロトコルですべてをラップすることで、モッククラスを作成します。次のように、問題のプロトコルに準拠するようにモッククラスを手動でロールします。
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
クラスで問題が発生するか、または重要なイニシャライザーがない場合があります。
マークされた答えに加えて何かを指摘したい-それがバグかどうかわからない。
NSObjectを何らかの方法でサブクラス化する場合(私の場合、NSObjectを内部的にサブクラス化するUIViewをサブクラス化する場合)、オーバーライドされた関数を@ objcで明示的に宣言する必要があります。私の場合、コンパイラ自体は次のようにクラッシュします:
セグメンテーション障害:11
したがって、次のクラス:
public class ClassA: UIView
{
@objc public func johnAppleseed() {
}
}
次の方法で単体テストを行う必要があります。
class ClassATests: XCTestCase {
func testExample()
{
class ClassAChildren: ClassA
{
@objc private override func johnAppleseed() {
}
}
}
}
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)
}
}
あなたが書いた制限のため、OCMockはSwiftでうまく動作しません(すべてのモックフレームワークはランタイムに強く依存しています)。
Swift、ただし、半手動からほぼ自動のモック生成まで、いくつかのモック作成フレームワークがあります。それらのいくつかは既に回答にリストされているので、別のフレームワークをお勧めします。著者の一人です。
https://github.com/MakeAWishFoundation/SwiftyMocky
私は詳細にはあまり触れませんが、小さな制限がありますが、私が見るところからは、Swiftフレームワーク(少なくとも私が知っているもの)、ジェネリックサポートを含む) 、@ objcメンバー、およびプロトコルの作成/変更中のモックの更新(ウォッチャーモード)。