- PR -

VS2008の単体テスト機能のテストコードの書き方について

1
投稿者投稿内容
ひろめん
会議室デビュー日: 2009/01/19
投稿数: 10
投稿日時: 2009-01-20 16:35
VS2008の単体テスト機能のテストコードの書き方について

VS2008PROで、単体テスト機能が標準装備されるようになっていますが、
これを利用してテストコードをコーディングしていくとき、
その書き方について迷っています。

現在複数のマシンに点在する電子書籍ファイルを一元管理するためのソフトを作成しているのですが、
リスト化した一覧を読み書きするメソッドの、テストコードの書き方がよくわかりませんので、
恐縮ですが皆さんのご意見を参考にさせていただきたいと思っています。

コード:
実コード
 1:        #region フィールド
 2:
 3:        /// <summary> 書籍データリスト </summary>
 4:        /// <!--key:SHA1ハッシュ値-->
 5:        private Dictionary<string, Book> _books { get; set; }
 6:        
 7:        #endregion
 8:
 9:        #region コンストラクタ
10:
11:        public class1(){
12:            _books = new Dictionary<string, Book>();
13:        }
14:
15:        #endregion
16:
17:        #region メソッド
18:
19:        /// <summary> 
20:        /// 書籍のリストをファイルに保存します。 
21:        /// </summary>
22:        /// <param name="filePath">保存するファイルのパス</param>
23:        public void SaveBookList(string filePath){
24:            //_booksをcsv形式でfilePathへ出力
25:        }
26:
27:        /// <summary> 
28:        /// ファイルから書籍のリストを取得します。 
29:        /// </summary>
30:        /// <param name="filePath">読み込むファイルのパス</param>
31:        public void LoadBookList(string filePath){
32:            //filePathからcsvを読み込み、_booksを構築(Clear→Add)
33:        }
34:
35:        #endregion
36:    }

VS2008のツールによって吐かれたテストコード
37:    public class class1Test
38:    {
39:        /// <summary>
40:        /// SaveBookList のテスト
41:        /// </summary>
42:        [TestMethod()]
43:        public void SaveBookListTest(){
44:            class1 target = new class1();
45:            string filePath = string.Empty; // TODO: 適切な値に初期化してください
46:            target.SaveBookList(filePath);
47:            Assert.Inconclusive("値を返さないメソッドは確認できません。");
48:        }
49:
50:        /// <summary>
51:        /// LoadBookList のテスト
52:        /// </summary>
53:        [TestMethod()]
54:        public void LoadBookListTest(){
55:            class1 target = new class1(); // TODO: 適切な値に初期化してください
56:            string filePath = string.Empty; // TODO: 適切な値に初期化してください
57:            target.LoadBookList(filePath);
58:            Assert.Inconclusive("値を返さないメソッドは確認できません。");
59:        }
60:    }



SaveBookListメソッドのテストを行おうとした場合(43行目)、
出力されたcsvファイルの正当性を判断する必要があると思うのですが、
そうすると47行目の前にLoadBookListないし、
別途csvファイルを読み込むメソッドを用意する必要が出てくるのではないかと思います。

そのためには「LoadBookListがテストをクリアしている」という前提が必要になると思うのですが、
読み込むcsvファイルの正当性のテストはどうなるのか、というところから突き詰めると、
どのようなテストコードを書けばよいのか悩んでしまいます。
仮にcsvを読み込むメソッドをテストクラス内に用意したとしても、
やはりそのテストは必要でしょうから結局堂々巡りになってしまう気がします。

皆さんはこのような場合、どうされるのがベストだとお考えでしょうか?
よこけん
大ベテラン
会議室デビュー日: 2006/01/31
投稿数: 216
投稿日時: 2009-01-21 00:43
そもそも、CSV に出力されるはずの文字列を単純に比較するだけで良いと思います。

コード:
1, aaa

2, bbb


と出力して欲しいならば、出力結果を "1, aaa\r\n2, bbb" という文字列と比較するだけです。


_________________
C#と諸々

[ メッセージ編集済み 編集者: よこけん 編集日時 2009-01-21 00:44 ]
ひろめん
会議室デビュー日: 2009/01/19
投稿数: 10
投稿日時: 2009-01-21 10:46
引用:
よこけんさんの書き込み (2009-01-21 00:43) より:
そもそも、CSV に出力されるはずの文字列を単純に比較するだけで良いと思います。



そういわれると「なるほど」という感じがしますね。

私としては出力メソッドのテストコードだから、
出力後のデータの検証が重要と考えていたのですが、
「出力する」という動作自体はFrameworkでサポートされていますので、
こちらが気にするところではなかったのかな・・・?

ただ不安なのが、文字列は正しくても出力自体のコード
(System.IO.File.WriteAllText等)を書き忘れていた場合に、
文字列の比較のみではミスを拾えないということですが、
この辺の一目見たらわかるような部分(ファイル出力に限らず)は、
テストコード内での検証は省いてもよいという意識でよいものでしょうか。

テスト駆動開発を意識したコーディングは初めてなので、
最初にどこまで突き詰めておくかの線引きがまだよくできていませんね(--;

[ メッセージ編集済み 編集者: ひろめん 編集日時 2009-01-21 11:00 ]
よこけん
大ベテラン
会議室デビュー日: 2006/01/31
投稿数: 216
投稿日時: 2009-01-21 11:07
すみません、書き方が悪かったです。
実際に生成された CSV ファイルの内容を読み取って、期待値と比較する、ということです。


> この辺は一目見たらわかるからということでテストコード内での検証は
> 省いてもよいという意識でよいものでしょうか。

テスト可能なことならば、テストを極力用意した方が良いです。
テスト不可能、あるいは可能だけど労力の割に効果が薄いのならばテストを省くこともありますが、
そういった処理は別のクラスに切り離した方が良いです。
そのクラスにはテスト可能なコードを極力持たせません。
そして、「依存性の注入」パターンを適用することで、
テスト時はそのクラスの代わりにフェイクやモックを使用することができます。
_________________
C#と諸々
ひろめん
会議室デビュー日: 2009/01/19
投稿数: 10
投稿日時: 2009-01-21 12:21
毎回素早い回答ありがとうございます。

なんとなくですけれど、よこけんさんの仰りたい事を理解できました。
常に依存関係を意識し、できるだけその関係を疎にしておく必要があるのですね。
今までは機能を意味を小分けすることをメインにクラスを作成していたのですが、
もう少し抽象的に考えてみて、インターフェースの実装などを積極的に
取り入れてみたいと思います。

今回の件については、ファイルのI/O部分を切り離して、
モックを作成してみることにします。
1

スキルアップ/キャリアアップ(JOB@IT)