プロトコル向けは銀弾ではない

4717 ワード

プロトコル向けは銀弾ではない

まずいくつかのテストの概念を理解します


doubleは置換と理解でき、すべてのシミュレーションテストオブジェクトの総称であり、代替体と呼ぶこともできます.一般的に、任意のテストディスプレイスメントオブジェクトを作成すると、指定したクラスのオブジェクトの代わりに使用されます.
stubは、特定のメソッドが呼び出されると、指定されたシミュレーション値を返すことができるテスト杭として理解される.テスト・インスタンスにいくつかのデータを提供するために伴生オブジェクトが必要な場合は、stubを使用してデータ・ソースの代わりに使用し、テスト設定時に一貫したシミュレーション・データを返すように指定できます.
spyは、状況を報告し、どの方法が呼び出されたのか、呼び出し中にどのパラメータが渡されたのかを追跡し続ける偵察として理解できる.特定のメソッドが呼び出されたかどうか、または正しいパラメータ呼び出しが使用されているかどうかなど、テストの断言を実現することができます.2つのオブジェクト間のいくつかのプロトコルまたは関係をテストする必要がある場合に便利です.
mockはspyと似ていますが、使用上は少し違います.spyはすべてのメソッド呼び出しを追跡し、後で断言を書かせますが、mockは通常、事前に期待を設定する必要があります.何が起こるかを教えて、テストコードを実行し、最終的な結果が事前に定義された期待と一致しているかどうかを検証します.
fakeは完全な機能実装と動作を備えたオブジェクトであり、動作はこのタイプの実際のオブジェクトと同じであるが、シミュレーションされたクラスとは異なり、テストをより容易にする.典型的な例は、メモリ内のデータベースを使用して、実際の本番環境のデータベースにアクセスするのではなく、データ永続化オブジェクトを生成することです.
実際には、これらの用語は、それらの定義とは異なり、交換することもできる.後でこの記事では、自分が「mockオブジェクトフレームワーク」だと思っているライブラリを見ますが、実際にはstubの機能も提供しており、動作を検証する方法も私が説明した「mock」ではなく「spy」に似ています.だからあまりこれらの語彙の細部に陥らないでください.これらの定義は、これらの概念を高レベルで区別し、異なるタイプのテストオブジェクトの動作を考慮するのに役立つため、より多く定義されています.
異なるタイプのシミュレーションテストオブジェクトの詳細な議論に興味がある場合は、Martin Fowlerの文章「Mocks Aren't Stubs」がこの問題に関する権威ある議論とされています.

スタート

class Webservice {
    func loadUser() -> User? {
        let json = self.load(URL(string: "/users/current")!)
        return User(json: json)
    }
    
    func loadEpisode() -> Episode? {
        let json = self.load(URL(string: "/episodes/latest")!)
        return Episode(json: json)
    }
    
    private func load(_ url: URL) -> [AnyHashable:Any] {
        URLSession.shared.dataTask(with: url)
        // etc.
        return [:] // should come from the server
    }
}
  • load--afnetworkingまたはあなたがカプセル化した汎用ネットワーク呼び出しインタフェース
  • loaduser -- networkapimanager
  • user -- model
  • episode -- model
  • Webservice -- datasource or datacenter

  • 今のところ悪くない.でもapimanagerをテストするなら
  • ネットワークリクエストを1つのものに置き換えます.
  • or pass in a mock URLSession using dependency injection.

  • △この言葉は理解できなかった???URLSessionに断言を書く?)
    まとめると、aの機能を検証するために、aはbを呼び出した.
  • は、bを置換することによって、aのシミュレーションデータに渡される.aの正確性をさらにテストすることができます.
  • または直接bに断言を書いて、bの入力結果と出力結果が予想に合っているかどうかを判断します.

  • We could also define a protocol that URLSession conforms to and then pass in a test instance.
    実は一時的にbに検査方法を追加しただけです.やはりmockメソッドと.これはまあまあ優雅でしょう.

    少し良いテストポイントの変更

    struct Resource{
    let url: URL
    let parse: ([AnyHashable:Any]) -> A
    }
    class Webservice {
    let user = Resource(url: URL(string: "/users/current")!, parse: User.init)
    let episode = Resource(url: URL(string: "/episodes/latest")!, parse: Episode.init)
    private func load(resource: Resource) -> A {
    URLSession.shared.dataTask(with: resource.url)
    // load asynchronously, parse the JSON, etc. For the sake of the example, we directly return an empty result.
    let json: [AnyHashable:Any] = [:] // should come from the server
    return resource.parse(json)
    }
    }

    これはリソースのパッケージであり,汎用的に入力と出力のセットを定義している.
  • 入力リソース:url
  • 出力リソース:オブジェクトA
  • テスト対象のuserとepisodeはmockのことを考えなくてもいいです.
    このような感じがしますが、本質はまだ理解していません:入出力がすべてあったので、1つの構造で、何も測定することはありません.では、問題が来ました.何が測らなければなりませんか.何が測らないのですか.構造本省にはプロシージャ呼び出しはありません.

    load関数はまだ測定します

    protocol FromJSON {
        init(json: [AnyHashable:Any])
    }
    
    struct Resource {
        let url: URL
    }
    
    class Webservice {
        let user = Resource(url: URL(string: "/users/current")!)
        let episode = Resource(url: URL(string: "/episodes/latest")!)
        
        private func load(resource: Resource) -> A {
    URLSession.shared.dataTask(with: resource.url)
    // load asynchronously, parse the JSON, etc. For the sake of the example, we directly return an empty result.
    let json: [AnyHashable:Any] = [:] // should come from the server
    return A(json: json)
    }
    }

    上は簡単に見えますが、柔軟性に欠けています(上は高集約の変更です)上のコードは、resourceにUser配列が含まれていることをどのように定義しますか.
    The protocol makes things simpler, but I think it doesn’t pay for itself, because it dramatically decreases the ways in which we can create a Resource
    struct Resource{
    let url: URL
    let parse: ([AnyHashable:Any]) -> A
    }
    protocol FromJSON {
        init(json: [AnyHashable:Any])
    }
    
    struct Resource {
        let url: URL
    }
    

    柔軟性の喪失はどこですか?