LoginSignup
1
2

More than 5 years have passed since last update.

private funcをテストしたいとき

Last updated at Posted at 2018-03-13

Dictionaryから値を取得する、Arrayのn番目の値を取得して何かする、計算するなど
privateなfuncを使用することは多いですよね。

しかし、そのprivateなfuncはロジックであることが多いです。つまりテストしたいですね:innocent:
しかし、privateにするとTestから呼び出すことができないので、テストができません。

privateなfunc

struct User {

    func save(dictionary: [String: String]) {
        guard let name = getname(from: dictionary) else {
            return
        }
        UserDefaults.standard.set(name, forKey: "key")
    }

    private func getName(from dictionary: [String: String]) -> String? {
        return dictionary["name"]
    }

}

例えば、このような例ですね。
外からはsave()を呼べればよい。そして、getName()はテストしたい。

インナークラスに隠す

インナークラスを作成して、そこにprivateなfuncを定義します

privateなfuncのprotocolを作る

protocol UserPrivateProtocol {
    static func getName(from dictionary: [String: String]) -> String?
}

extension UserPrivateProtocol {

    static func getName(from dictionary: [String: String]) -> String? {
        return dictionary["name"]
    }

}

まずは、privateにしたいfuncをprotocolに実装します。
このprotocolはinternalなので準拠したクラスを作れば、getName()は使えてしまうのですが、それは目をつむります笑

インナークラスをprotocolに準拠させる

struct User {

    private struct Private: UserPrivateProtocol {
    }

    func save(dictionary: [String: String]) {
        guard let name = Private.getName(from: dictionary) else {
            return
        }
        UserDefaults.standard.set(name, forKey: "key")
    }

}

インナークラスをprivateにすることで外から触ることができなくなります。
しかし、User内ではちゃんと使えます。

Testする

let dictionary = ["name": "tarou"]

struct UserPrivateMock: UserPrivateProtocol {

}

XCTAssertEqual(dictionary["name"], UserPrivateMock.getName(from: dictionary))

テストするときは、privateなfunc用に作ったprotocolに準拠したMockクラスを作成します。
そのMockクラスを使用することでテストが可能になります。

課題

protocolのdefault実装を利用して、Mockを作ってテストするのですが、getName()の実装がdefaultのままという前提のテストになってしまいます。

インナークラス内にprivateにしたいfuncを定義

インナークラス作成

struct User {

    struct Private {
        static func getName(from dictionary: [String: String]) -> String? {
            return dictionary["name"]
        }
    }

    func save(dictionary: [String: String]) {
        guard let name = Private.getName(from: dictionary) else {
            return
        }
        UserDefaults.standard.set(name, forKey: "key")
    }

}

インナークラスにprivateにしたいfuncを定義します。
User.Private.getName()でアクセスできてしまうのですが、それは目をつむりましょう笑

Test

let dictionary = ["name": "tarou"]

XCTAssertEqual(dictionary["name"], User.Private.getName(from: dictionary))

これでテストができます。

まとめ

2つの方法を思いついたので書きましたが、特に問題がないならinternalで定義するのが簡単で良いと思います:relaxed:

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2