web-dev-qa-db-ja.com

モックインSwift

Swiftでオブジェクトをどのようにモックしますか?

Mirrorプロトコルは有望に思えましたが、現時点ではあまり効果がありません。

これまでのところ、私が見つけた唯一のアプローチは、模擬クラスのすべてのメソッドをサブクラス化してオーバーライドすることです。もちろん、これは真のモックではなく、理想とはほど遠い、多くの作業です。

他のアイデアはありますか?

なぜOCMockではないのですか?

source から:

言語ブリッジ機能を使用してOCMockを使用できますか?

はい、ただし制限があります。あなたが勇敢なら。現在のところ、これは非常に実験的なものです。 OCMockがSwiftを完全にサポートするという保証はありません。

既知の制限:

  • テストはObjective-Cで作成する必要があります
  • モックされるべきオブジェクトはNSObjectから継承する必要があります
  • クラスメソッドのスタブ/期待/検証なし
51
hpique

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つは、モッククラスが呼び出されたことを確認するための「検証」メソッドです。これを手動で追加することもできますが、自動追加はナイスです。

27
Drew Beaupre

プロトコルですべてをラップすることで、モッククラスを作成します。次のように、問題のプロトコルに準拠するようにモッククラスを手動でロールします。

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クラスで問題が発生するか、または重要なイニシャライザーがない場合があります。

6
Mark

マークされた答えに加えて何かを指摘したい-それがバグかどうかわからない。

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() {

            }
        }
    }
}
2
hris.to

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)
    }

}
2
sundance

MockFive を使用すると、この種のモックを実現できます。

その要点は、モックしたいソースクラスの「手動」でモックを作成する必要があることです。

ただし、そのメソッドを動的にスタブ化できるため、必要な場所で使用して動作を構成できます。

私はそれを使用する方法についての記事を書きました here

1
Daniel Burbank

あなたが書いた制限のため、OCMockはSwiftでうまく動作しません(すべてのモックフレームワークはランタイムに強く依存しています)。

Swift、ただし、半手動からほぼ自動のモック生成まで、いくつかのモック作成フレームワークがあります。それらのいくつかは既に回答にリストされているので、別のフレームワークをお勧めします。著者の一人です。

https://github.com/MakeAWishFoundation/SwiftyMocky

私は詳細にはあまり触れませんが、小さな制限がありますが、私が見るところからは、Swiftフレームワーク(少なくとも私が知っているもの)、ジェネリックサポートを含む) 、@ objcメンバー、およびプロトコルの作成/変更中のモックの更新(ウォッチャーモード)。

0
Andrzej Michnia