Slide 1

Slide 1 text

© Chatwork Swiftでテスト時のみ 『private』にアクセスする方法 2023年7月14日 モバイルアプリケーション開発部 中山 龍 Chatwork株式会社

Slide 2

Slide 2 text

自己紹介 2 中山 龍 (なかやま りゅう) ● Chatwork株式会社 ○ モバイルアプリケーション開発部 ○ iOSエンジニア ○ 2023年4月 新卒として入社 ○ 社内最年少 (2002年6月生まれの21歳) ● 愛知県在住 ○ フルリモート勤務 ● 初めてのLTで緊張してます! @ryu_develop

Slide 3

Slide 3 text

3 © Chatwork はじめに

Slide 4

Slide 4 text

4 テストを書いている時に...

Slide 5

Slide 5 text

5 テストを書いている時に 『private』にアクセスしたいな って場面、ありませんか?

Slide 6

Slide 6 text

例えば...テスト中に Privateな ● 値をリセットしたい ● 値を追いたい ● メソッドをテストしたい 6   は避けた方が良い場合も...   終盤でお話しします

Slide 7

Slide 7 text

テストのためにprivateにアクセスしたいとき、どうすればいいんだろう? 7

Slide 8

Slide 8 text

今回扱う状況・問題 1

Slide 9

Slide 9 text

今回想定する状況 9 class Sample { public static let shared = Sample() private var value = 0 } ● privateなプロパティ『value』を持っているclass ● シングルトン

Slide 10

Slide 10 text

このclassをテストする際の問題点 10 ● classをテストごとにインスタンス化ができない → 🚨 シングルトンならではの問題が起きる

Slide 11

Slide 11 text

このclassをテストする際の問題点 11 ● classをテストごとにインスタンス化ができない ● privateなプロパティをテストごとにリセットできず、値がテ ストを超えて引き継がれてしまう → 🚨 シングルトンならではの問題が起きる

Slide 12

Slide 12 text

このclassをテストする際の問題点 12 ● classをテストごとにインスタンス化ができない ● privateなプロパティをテストごとにリセットできず、値がテ ストを超えて引き継がれてしまう → テストの結果が実行順に左右されてしまう 🚨 シングルトンならではの問題が起きる

Slide 13

Slide 13 text

13 変更した値が次のテストにも引き継 がれるのを回避したい

Slide 14

Slide 14 text

14 『private』なプロパティにアクセス して、値をリセットしたい

Slide 15

Slide 15 text

何も考えずに『private』にアクセスするとしたら? 2

Slide 16

Slide 16 text

class Sample { public static let shared = Sample() private var value = 0 func getter() -> Int { return value } func setter(value: Int) { self.value = value } } 何も考えずに『private』にアクセスするとしたら 16 getterとsetterを持たせる ↓ privateへのget / set が可能 になる

Slide 17

Slide 17 text

17 アクセスできるけど... これだとprivateにした意味がない! テストの時だけgetterとsetterを使える ようにできないでしょうか?

Slide 18

Slide 18 text

『テスト』を判定してアクセスを可能にする 3

Slide 19

Slide 19 text

『テスト』のビルドを判定する 19 iOS開発ではビルドの種類を判定する仕組みがある #if DEBUG // デバッグ時の処理 #endif 以下は、「デバッグビルドの際に処理が走る」という例 デバッグ ビルド デバッグ時 の処理 YES NO

Slide 20

Slide 20 text

『テスト』のビルドを判定する 20 この分岐は「Build Configuration」によって 判定されている デフォルトで存在するBuild Configurationは ● Debug ● Release の2種類

Slide 21

Slide 21 text

『テスト』のビルドを判定する 21 この「Build Configuration」に「Test」を追加すれば、 「テスト」のビルドを判定できるのではないか?

Slide 22

Slide 22 text

『Build Configuration』を追加する(手順解説) 4

Slide 23

Slide 23 text

Build Configurationの追加方法 23 PROJECT 内の Info で Configurations左下の+ をクリック し、Duplicate “Debug” Configurationを選択 生成されたものの名前をTestにする 1.

Slide 24

Slide 24 text

Build Configurationの追加方法 24 Edit Schemeを開き、TestのBuild ConfigurationでTestを 選択する 2.

Slide 25

Slide 25 text

Build Configurationの追加方法 25 PROJECT の Build Settings にある Active Compilation Conditions で Testキー の値を TESTにする 3.

Slide 26

Slide 26 text

26 こうすることでBuild Configurationが追加され、 #if TESTで「テスト」を判定できるようになる

Slide 27

Slide 27 text

実際に分岐させる 5

Slide 28

Slide 28 text

class Sample { public static let shared = Sample() private var value = 0 #if TEST func getter() -> Int { return value } func setter(value: Int) { self.value = value } #endif } テストで分岐させてみる 28 getterとsetterを #if TEST 〜 #endifで囲むと、 テスト以外では存在しないこと にできる

Slide 29

Slide 29 text

つまり 29 class Sample { public static let shared = Sample() private var value = 0 func getter() -> Int { return value } func setter(value: Int) { self.value = value } } テストビルド時 class Sample { public static let shared = Sample() private var value = 0 } テスト以外のビルド時 テストビルド以外の際にはgetter とsetterが無い扱いとなる

Slide 30

Slide 30 text

30 通常時はprivateのメリットを生かしつつ、 テストの時には『private』にアクセス できるようになった!!

Slide 31

Slide 31 text

補足 31 「#if TEST」で囲まれた部分を呼び出すコードを書くと Xcodeに警告される。 それは、おそらく静的解析がテストビルド以外の Configurationで走っているためである。(予想) 警告を無視しても問題なくビルドできる

Slide 32

Slide 32 text

他にも... 32 この#if TEST 〜 #endifの書き方を応用すること で、テスト内で自由に『private』にアクセスした り、テスト時のDIに活用したりすることができる

Slide 33

Slide 33 text

33 でも...注意する点があります

Slide 34

Slide 34 text

テストで『private』にアクセスする際の注意 6

Slide 35

Slide 35 text

注意点 35 冒頭でも少しお話ししたようにprivateに対するテストは避 けた方が良い場合もあります

Slide 36

Slide 36 text

注意点 36 自動テストを書く理由の一つとして「リファクタリングの支えになる」という点があ ります ● リファクタリング:「外部から見た振る舞いを変えずに内部の実装をきれいにするこ と」 t-wadaさんのブログ: プライベートメソッドのテストは書かないもの? より引用し、要約 https://t-wada.hatenablog.jp/entry/should-we-test-private-methods

Slide 37

Slide 37 text

注意点 37 自動テストを書く理由の一つとして「リファクタリングの支えになる」という点があ ります ● リファクタリング:「外部から見た振る舞いを変えずに内部の実装をきれいにするこ と」 しかし、privateに対するテストは「内部の実装に対するテスト」になってしまうこ とが多い → リファクタリングの妨げになりがち t-wadaさんのブログ: プライベートメソッドのテストは書かないもの? より引用し、要約 https://t-wada.hatenablog.jp/entry/should-we-test-private-methods

Slide 38

Slide 38 text

注意点 38 privateに対してのテストを書いてしまうと 自動テストで積極的にリファクタリングを行いたい ↓ 自動テストがリファクタリングの妨げになる という場合も... → テスト時に『private』にアクセスする場合は、この ような状況に気をつけましょう

Slide 39

Slide 39 text

まとめ 7

Slide 40

Slide 40 text

まとめ 40 ● Build Configurationを追加することで、テストのビルドを判 定して、適切な処理の分岐が可能になる ○ 今回紹介した方法で 「#if TEST」 が使えるようになる

Slide 41

Slide 41 text

まとめ 41 ● #if TESTが使えると... ○ 通常ビルドではprivateの恩恵を受けつつ、テスト時には privateにアクセスして、値のリセットなどができる ○ テスト用にDIをする処理を書くなど、使い方によって様々 なことが可能になる

Slide 42

Slide 42 text

まとめ 42 ● ただし、privateに対してテストを書く・値を操作する時には、 それがリファクタリングの妨げにならないように気をつける

Slide 43

Slide 43 text

働くをもっと楽しく、創造的に