[速報レポート] try! Swift TOKYO 2017 2日目 午前I #tryswiftconf
はじめに
こんにちは!2日目も try! Swift 始まりました!! 引き続きレポートします。
「テスト可能なコードを書くということの2つの側面」 Brandon Williamsさん
概要
関数を完全にテスト可能にするためのものが2つあります。作用の分離と共作用を表面化です。この2つの側面の背景にある理論を探り、どのようにすればテスト容易なコードに導けるかを示します。また、最近オープンソース化されたコードベースを基にKickstarterで我々がどのように実践しているかも紹介します。
スライド
(資料が公開され次第更新します)
説明
- テストについてたくさん話します。テスト可能なコードを書くにはどうしたらいいかについて話します。
- なぜテストをするのか
- それほど深い話はない。経験が必要。方法を知らないとできない。
- バグを見つけたり、クラッシュを防げる
- 私の場合には、コードを書くのはテストができるコードだけを書くようにしている
- 関数的な書き方で書いていく
- 完全にドキュメント化して、期待値を書いてそれにそったものを書いていく
- テストが難しいケースがある
- computeという関数で説明します。
- stringを引数にintを返す関数である
- コンソールに何かを表示するのがこの関数の要件
- ファイルのコンテンツを読んで整数を掛けて、コンソールにprintして結果を返す
- このような関数はみんな書いていると思う
- ファイルのパスがinput、そしてoutputを出していく
- 他のfunctionからファイルパスを得る
- ファイルを読むのも環境に依存する別のfunctionになっている
- 関数の戻り値では説明されていない挙動がある
- この関数をテストするにはファイルを特定のロケーションに置く必要がある
- inputには明示的なものと暗黙的なものがある
- outputにも明示的なものと暗黙的なものがある
- outputのテストについて
- 副作用があるため難しい
- 実行によって外の世界に変化がある
- 実行後の状態について確認する必要がある
- どうやったら副作用をうまく扱えるか?
- 副作用をなるべくコードの境界に動かしていくことが必要
- compute関数に戻って副作用を見ていく
- 戻り値をタプルにしていく
- inputのテストについて
- 完全にコントロールされて明確に定義されたデータ
- 共作用というものがある
- 優れた定義があまりない
- 新しい研究トピック
- DIと呼ぶ人もいるかもしれない
- ある特定の世界の条件がなければ実行ができないというもの
- 1つのstructに全て入れてしまう。そういうような環境にしてしまう。
- Dateは非常に大きな共作用となる。Dateは毎回違うのでテストしにくい。
- そのためプロトコルにしてコントロールする。
- Languageも非常に大きな共作用となる。
- bundle、scheduler、UserDefautも共作用となるのでコントロールしたい。それ以外にもたくさんある
- 一箇所にまとめてしまうほうが楽
- リファクタ
- Bundle.main.pathのテストをするために、プロトコルを入れていく。
- 成功の場合と失敗の場合をテストする
- contentsOfFileもプロトコルにしていく
- 複雑に見えるが関数の中に入れている。この関数が何をやっているかわかるようになる。
「誰もが知りたいSequenceとCollectionのすべて」 Soroush Khanlouさん
概要
SequenceとCollectionについて深く掘り下げてみましょう。LinkedListを実装することでSequenceとIteratorがどのように相互作用するかを見ていきます。この講演ではCollectionと関連するプロトコルと型(Index、Sliceable、MutableCollectionなど)がそれぞれどのように相互作用するかについて説明します。最終的にSequenceとCollectionについて知りたかったこと以上のことがわかるようになるでしょう。
スライド
(資料が公開され次第更新します)
説明
- SequenceとCollectionのすべてについて話します。
- まず、Sequenceというプロトコルがある。非常にシンプルに構築されている。
- その後 Collecton、BidirectionalCollection、RandomAccessCollection、RangeReplaceableCollectionを説明する。
- Sequence
- Elementのリスト
- 有限である
- 1回しかイテレートできない
- associatedtypeにIteratorがある
- makeIterator()という関数がある
- Iterator
- Elementがあって、これがイテレートしていく型である
- next()という関数がある
- LinkedList
- 順次見ていくことができるSequence
- enumを使って定義するのが良いと思う
- indirectというキーワード
- 2つのケース
- 値があればT
- 最後がend
- Sequenceに適合がさせていくことでmapなどが使えるようになる
- LinkedListIterator
を定義する(IteratorProtocolに適合する) - currentでステートを持つ
- next()がOptionalのTを戻す
- enumがあるのでいろんなことができる
- 中に何があるのか見れる
- switch文で値があれば次の要素を返す
- currentが次の要素を指すようになる
- 最後はnilを返す
- filterを使ってもよいがArrayを作ってしまうため直接.countを実行したい
- そのためにはcountを全てのSequenceに追加する
- extensionでcountを追加する
- そのためにはcountを全てのSequenceに追加する
- Each Pair
- 全ての連続Elementをグループ化するもの
- 差分を見るときに使う
- zipとSequenceのdropFirst()を使う
- これはこれでいいが実際はメソッドチェーンしたい
- そのためにSequenceのextensionにeachPair()を定義する
- Self.SubSequence : Sequenceの制約を追加する
- SubSequenceの中のElementに対して制約を追加する
- 全ての連続Elementをグループ化するもの
- Collectionについて
- Sequenceを引き継ぐ
- 複数回イテレートできる
- Collectionプロトコルについて
- Indexというassociatedtypeがある
- startIndexとendIndex
- subscript関数
- index関数
- APIErrorをCollectionにしたい
- そのためにextensionが必要(Collectionに準拠させる)
- BidirectionalCollection
- Collectionから引き継いでいる
- 前にも後ろにも戻れる
- index(before)がある
- lastというプロパティがある
- 説明した以外にもいろんなコレクションがある
- RandomAccessCollectionはアクセスしたい要素にすぐにアクセスできる
- RangeReplaceableCollectionは真ん中にあるコレクションにアクセスできる