Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- TDD自分用メモ
- # Part 3
- ## TDDのパターン
- * ソフトウェアをどのようにテストするのか▶︎自動テストを作成してテストする
- * テストはテスト間でどのように影響すべきか▶︎影響すべきでない
- * 何をテストすべきか▶︎作成する必要があると思われる全てのテストのリストを書く
- * いつテストを作成すべきか▶︎テスト対象のコードを作成する前
- * アサートをいつ作成すべきか▶︎最初に作成すべき
- 例
- ソケット経由で通信を行い、通信を終えるとabcという文字列が読み込まれる
- ``` test.java
- testCompleteTransaction(){
- ..
- assertTrue(reader.isClosed());
- asserEquals("abc", reply.contents());
- }
- ```
- replyはソケットからきている
- ``` test.java
- testCompleteTransaction(){
- ..
- Buffer reply = reader.contents();
- assertTrue(reader.isClosed());
- asserEquals("abc", reply.contents());
- }
- ```
- ソケットはサーバーとの接続時に作成する
- ``` test.java
- testCompleteTransaction(){
- ..
- Socket reader = new Socket("localhost",defaultPort());
- Buffer reply = reader.contents();
- assertTrue(reader.isClosed());
- asserEquals("abc", reply.contents());
- }
- ```
- その前にサーバーをオープンする必要がある
- ``` test.java
- testCompleteTransaction(){
- Server writer = new Server(defaultPort(),"abc");
- Socket reader = new Socket("localhost",defaultPort());
- Buffer reply = reader.contents();
- assertTrue(reader.isClosed());
- asserEquals("abc", reply.contents());
- }
- ```
- * テストでどのデータを使用するのか▶︎テストを読みやすくするデータを使用すべき、あるいは実データ
- 実データを使うと役立つケース
- ```
- * 外部イベントをトレースしてリアルタイムシステムをテストする場合
- * 現在のシステムと旧システムの出力を比較する場合
- * シミュレーションをリファクタリングして終了時にまったく同じ答えを期待する場合
- ```
- * データの目的をどのように表現するか▶︎テスト自体に期待する結果と実際の結果を含め、その関係を明示する
- 定数を定義する
- ``` test.java
- testCalcResult(){
- Bank bank = new Bank();
- bank.addRate("USD", "GBP", STANDARD_RATE);
- bank.commission(STANDARD_COMMISION);
- Money result = bank.convert(new Note(100, "USD"), "GBP");
- assertEquals(new Note(49.25,"GBP"),result);
- }
- ```
- 計算過程を明示する
- ``` test.java
- testCalcResult(){
- Bank bank = new Bank();
- bank.addRate("USD", "GBP", 2);
- bank.commission(0.015);
- Money result = bank.convert(new Note(100, "USD"), "GBP");
- assertEquals(new Note(100 / 2 * (1 - 0.015),"GBP"),result);
- }
- ```
- ## レッドバーのパターン
- * どのテストを選択すべきか▶︎ナニカを教えてくれて実装できる自信をつけさせてくれるテストを選択すべき
- * どのテストから開始すべきか▶︎何もしない操作のバリエーションをテストすることから開始する
- * 自動テストをどのように普及させるか▶︎テストを用いた説明を求め、提供するフィードバックループ
- * 外部で制作されたソフトのテストをいつ作成するか▶︎パッケージ内の新機能を初めて使用する前に作成する
- * 技術的な議論の話題が横道に逸れないようにするには▶︎脱線的な考えが浮かんだら、TODOリストにテストを追加して本題に戻る
- * 欠陥が発見されたら▶︎失敗する最小のテストを作成し、実行した後に修正する(回帰テスト)
- * 疲れたり行き詰まったりしたら▶︎休憩をとる
- * 迷っている時▶︎コードを捨て、やり直す
- * 使用すべきモノ▶︎安価な机と良質な椅子
- ## テストのパターン
- * 大きすぎるテストケースをどのように動作させるか▶︎大きなテストケースで失敗する部分を抜き出して小さなテストケースを作成し、動作するようになってから大きなテストケースに再度導入する
- * 複雑なリソースに依存するオブジェクトのテストはどのように行うか▶︎定数を返す仮バージョンのリソースを作成する
- モックオブジェクトを使う▶︎モックオブジェクト用のテストを本当のオブジェクトにも適用できるようにすることでモックオブジェクトの振る舞いへのリスクに対処する
- * オブジェクトが正しく通信していることをどのようにテストするか▶︎テスト対象のオブジェクトを本来通信すべきオブジェクトではなく、テストケースと通信させる
- * メッセージが呼び出される順番の正しさをどのようにテストするか▶︎メッセージ実行毎にログ文字列に追加し、文字列を検査する
- * 呼び出されそうにないエラーコードをどのようにテストするか▶︎実際に処理するのでなく、例外を起こす特別なオブジェクトを用意して、エラーコードを呼び出す
- * 一人でプログラミングをしているとき、どのように終えるか▶︎最後のテストを失敗のままにして終える
- * チームでプログラミングしているとき、どのように中断するか▶︎全てのテストを動作させる
- ## グリーンバーのパターン
- * テストが失敗した場合、最初にどのような実装をするか▶︎定数を返し、テストが動作したら次第に定数を変数を使用した式に置き換える
- * テストで最も控えめに抽象化を行うには▶︎2つ以上の例がある場合だけ抽象化する
- ``` sample.java
- public void testSum(){
- assertEquals(4,plus(3,1));
- }
- private plus(int augend, int addend){
- return 4;
- }
- ```
- 例を増やしてから実装する
- ``` sample.java
- public void testSum(){
- assertEquals(4,plus(3,1));
- assertEquals(7,plus(3,4));
- }
- private plus(int augend, int addend){
- return augend + addend;
- }
- ```
- * シンプルな操作をどのように実装するか▶︎ただ実装する
- 「動作する」と同時に「きれいなコード」を解決しようとしないこと。
- まず「動作する」、その後ゆっくりと「きれいなコード」を実現する
- * オブジェクトのコレクションで動作する操作をどのように実現するか▶︎まずコレクションを使わずに実装し、それからコレクションを用いて動作させる
- ``` sample.java
- public void testSum(){
- assertEquals(5, sum(5));
- }
- private sum(int value){
- return value;
- }
- ```
- ``` sample.java
- public void testSum(){
- assertEquals(5, sum(5, new int[] {5}));
- }
- private sum(int value, int[] values){
- return value;
- }
- ```
- ``` sample.java
- public void testSum(){
- assertEquals(5, sum(5, new int[] {5}));
- }
- private sum(int value, int[] values){
- int sum = 0;
- for (int i = 0; i < values.length; i ++)
- sum += values[i];
- return sum;
- }
- ```
- ``` sample.java
- public void testSum(){
- assertEquals(5, sum(new int[] {5}));
- assertEquals(12, sum(new int[] {5, 7}));
- }
- private sum(int[] values){
- int sum = 0;
- for (int i = 0; i < values.length; i ++)
- sum += values[i];
- return sum;
- }
- ```
- ## xUnitのパターン
- * テストが正しく動作することをどのようにチェックするか▶︎コードが動作するかの判断を自動化する論理式を作成する
- * 複数のテストで必要とされる共通オブジェクトをどのように作成するか▶︎テスト内のローカル変数をインスタンス変数に変換し、setUp()をオーバーライドしインスタンス変数を初期化する
- * フィクスチャ内でどのように外部リソースを解放するか▶︎tearDown()をオーバーライドする
- * 1つのテストケースをどのように表現するか▶︎testで始まるメソッドとして表現する
- * 想定される例外をどのようにテストするか▶︎想定される例外とキャッチして無視し、例外が起きなかった場合にテストが失敗するようにする
- * すべてのテストそどのように一度で実行するか▶︎全てのテストを集約するスイートを作成する
- ## TDDにおけるデザインパターンの使用
- | pattern | テスト作成 | リファクタリング |
- |:--|:--:|:--:|
- |Command|o|-|
- |ValueObject|o|-|
- |NullObject|-|o|
- |TemplateMethod|-|o|
- |PluggableObject|-|o|
- |PluggableSelector|-|o|
- |FactoryMethod|o|o|
- |Imposter|o|o|
- |Composite|o|o|
- |CollectingParameter|o|o|
- ## デザインパターン
- ### Command
- * 概要
- 処理呼び出しをメッセージでなくオブジェクトとして表現
- * ユースケース
- シンプルなメソッド呼び出しよりも、複雑な処理を呼び出す必要がある場合
- ### ValueObject
- * 概要
- 作成後は値が変化しないオブジェクト、別名参照の問題を避ける
- * ユースケース
- 広く共有されるが識別することは重要でないオブジェクト、元のオブジェクトを変更せずに新しいオブジェクトを返しても問題ない場合(計算時の値など)
- ### NullObject
- * 概要
- 処理の基本ケースをオブジェクトで表現する
- * ユースケース
- 特殊な状況を表現したい 通常のオブジェクトと同じプロトコルをそのオブジェクトに持たせる
- ### TemplateMethod
- * 概要
- 処理の不変な手順を抽象メソッドで表現し、継承で特化する
- * ユースケース
- 将来的に洗練できる不変の処理手順の表現 手順と詳細の分離
- ### PluggableObject
- * 概要
- 別のオブジェクトを呼び出すことで複数の実装によるバリエーションを表現する
- * ユースケース
- バリエーションの表現 条件分岐の重複を避ける
- ``` SelectionTool.java
- Figure selected;
- public void mouseDown(){
- selected = findFigure();
- if(selected != null)
- select(selected);
- }
- public void mouseMove(){
- if(selected != null)
- move(selected);
- else
- moveSelectionRectangle();
- }
- public void mouseUp(){
- if(selected == null)
- selectAll();
- }
- ```
- ``` SelectionTool.java
- SelectionMode mode;
- public void mouseDown(){
- selected = findFigure();
- if(selected != null)
- mode = SingleSelection(selected);
- else
- mode = MultipleSelection();
- }
- public void mouseMove(){
- mode.mouseMove();
- }
- public void mouseUp(){
- mode.mouseUp();
- }
- ```
- ### PluggableSelector
- * 概要
- 異なるインスタンスの異なるメソッドを動的に呼び出すことでよけいなサブクラスを避ける
- * ユースケース
- インスタンス毎に異なる振る舞い switchをなくしたい
- サブクラス化はするまでもなく...
- ```sample.java
- abstract class Report{
- abstract void print();
- }
- class HTMLReport extends Report{
- void print(){
- ...
- }
- }
- class XMLReport extends Report{
- void print(){
- ...
- }
- }
- ```
- swithも辛い
- ```sample.java
- class Report{
- String printMessage;
- Report(String printMessage){
- this.printMessage = printMessage;
- }
- void print(){
- switch (printMessage){
- case "printHTML":
- printHTML();
- break;
- case "printXML":
- printXML();
- break;
- }
- }
- void printHTML(){}
- void printXML(){}
- }
- ```
- リフレクションによる解決策
- ```
- void print(){
- Method runMethod = getClass().getMethod(printMessage, null);
- runMethod.invoke(this, new Class[0]);
- }
- ```
- ### FactoryMethod
- * 概要
- コンストラクタではなく、メソッドを呼び出すことでオブジェクトを生成する
- * ユースケース
- オブジェクトを生成するための柔軟性が必要 コンストラクタでは表現力と柔軟性を欠く
- ### Imposter
- * 概要
- 既存プロトコルの新しい実装を導入することでバリエーションを導入する
- * ユースケース
- 処理に新しいバリエーションを導入したい NullObject, CompositeもImposterの例
- ### Composite
- * 概要
- オブジェクト群の振る舞いの組合せを一つのオブジェクトで表現する
- オブジェクトのコレクションを単独のオブジェクトと同じように扱うことができる
- * ユースケース
- オブジェクト群の振る舞いの組合せを1つの振る舞いとして持つオブジェクトを実装したい
- ### CollectingParameter
- * 概要
- 処理結果を集約するオブジェクトを多くの異なるオブジェクト間で引数として順に回していく
- * ユースケース
- 複数のオブジェクトにまたがる操作結果の収集
- ### Singleton
- * 概要
- グローバル変数のような
- * ユースケース
- 使わない 設計に時間を費やすこと
- ## リファクタリング
- ### 相違の解消
- * 似たような2つのコードをどのように統一するか▶︎徐々に近づけていき、完全に同じ場合にだけ統一する
- ### 変更の独立化
- * 複数の部分からなるメソッドやオブジェクトの一部をどのように変更するか▶︎まず変更しなければ行けない部分を独立化する(メソッド抽出、オブジェクト抽出、メソッドオブジェクトを使う)
- ### データの移行
- * ある表現からどのように移行するか▶︎一時的にデータを重複させる
- 内部から外部へ移行する場合
- * 新しい形式のインスタンス変数を追加
- * 古い形式を設定している箇所で新しい形式の変数を設定
- * 古い形式を使用している箇所で新しい形式の変数を使用するよう変更
- * 古い形式を削除
- * 新しい形式を反映するよう外部インターフェースを変更
- APIを先に変更したい場合
- * 新しい形式の引数を追加
- * 新しい形式の引数から古い形式の内部表現に変換
- * 古い形式の引数を削除
- * 古い形式の使用箇所を新しい形式で置換
- * 古い形式を削除
- ### メソッド抽出
- * 長く複雑なメソッドをどのように読みやすくするか▶︎そのメソッドの小さな部分を別のメソッドに変換し、新しいメソッドを呼び出す
- 1. メソッド内で単独でメソッドとして成り立つ領域を見つける
- 1. 抽出する領域のスコープ外で宣言されている一時変数への割当が行われていないことと確認する
- 1. コードを新しいメソッドにコピーしてコンパイルする
- 1. 新しいメソッドの引数に元のメソッドの一時変数と引数を追加する
- 1. 元のメソッドから新しいメソッドを呼び出す
- ### メソッドインライン化
- * ねじれて散らばった制御フローをどのようにシンプルにするか▶︎メソッド呼び出しをメソッド本体に置き換える
- 1. メソッドをコピーする
- 1. メソッド呼び出しの場所にコピーしたメソッドをペーストする
- 1. 仮引数を実引数に置換
- ### インターフェース抽出
- * 操作の別の実装をどのように導入するか▶︎共有操作を含むインターフェースを作成する
- 1. インターフェースを宣言する
- 1. 既存クラスにインターフェースを実装させる
- 1. インターフェースに必要なメソッドを追加する
- 1. 可能な場所で型宣言をクラスからインターフェースに変更する
- ### メソッド移動
- * メソッドを本来属すべきに移動する▶︎属すべきクラスにメソッドを追加し、そのメソッドを呼び出す
- 1. メソッドをコピー
- 1. ターゲットクラスにメソッドをペースト
- 1. 元のオブジェクトがメソッド内で参照されていたらそのオブジェクトを引数として渡す。元オブジェクトの変数が参照されていたらその変数を引数として渡す。元オブジェクトの変数が設定されていたら諦める
- 1. 元メソッドの本体を新しいメソッド呼び出しに置き換える
- 別のオブジェクトbounds(Rectangle)に4つのメッセージが送信されている
- ``` Shape.java
- int width = bounds.right() - bounds.left();
- int height = bounds.bottom() - bounds.top();
- int area = width * height;
- ```
- メソッドに移動する
- ``` Rectangle.java
- public int area(){
- int width = this.right() - this.left();
- int height = this.bottom() - this.top();
- return width * height;
- }
- ```
- ``` Shape.java
- int area = bounds.area();
- ```
- ### メソッドオブジェクト
- * 複数パラメータとローカル変数を必要とする複雑なメソッドをどのように表現するか▶︎そのメソッドを元にオブジェクトを作成する
- 1. メソッドと同じパラメータを持つオブジェクトを作成する
- * ローカル変数をオブジェクトのインスタンス変数にする
- * run()メソッドを作成し、本体は元のメソッド本体と同じにする
- * 元のメソッドで新しいオブジェクトを生成し、run()を呼び出す
- ### 引数追加
- * メソッドに引数を追加したい
- 1. メソッドがインターフェースの一部であればインターフェースに引数を追加する
- * メソッドに引数を追加する
- * コンパイルエラーを利用して、変更必要な箇所を確認する
- ### メソッドからコンストラクタへの引数の移行
- * メソッドの引数をコンストラクタの引数に移行したい
- 1. コンストラクタに引数を追加する
- * 引数と同じ名前のインスタンス変数を追加する
- * コンストラクタ内でそのインスタンス変数を設定する
- * 1つずつ、parameterへの参照をthis.parameterへの参照に変換する
- * その引数への参照がなくなったらメソッドと呼び出し元からその引数を削除する
- * 参照から余分になったthis.を取り除く
- * そのインスタンス変数の名前を正しくする
- ## TDDの習得
- ### 手順はどの程度の大きさであるべきか
- ### テストしなくてもよいのは何か
- ### よいテストがあることをどのように知るのか
- ### TDDはフレームワークをどのように導くのか
- ### どれだけのフィードバックが必要なのか
- ### いつテストを削除すべきか
- ### 言語や環境がどのようにTDDに影響するのか
- ### 巨大システムをTDDできるか
- ### アプリケーションレベルのテストで開発を駆動することができるか
- ### 途中でどのようにTDDに切り替えるのか
- ### TDDは誰のためにあるのか
- ### TDDは初期条件に敏感なのか
- ### TDDはパターンとどのように関係するのか
- ### なぜTDDは動作するのか
- ### TDDの由来
- ### TDDとXP
- ###
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement