ユニットテスト メモ

テストの目的:ソースコードを保護すること

テストではDBやファイルシステムにアクセスしないこと
目的:テストの実行時間を短くして、少しでも修正をしたら気軽にテストを実行できるようにするため。
→DBアクセス、を最低限にし、実行時間を短く保つのであれば、DBに接続してもよい
→DBからデータを取得する際の絞り込み条件などを変更する場合は、実際にDBに接続しないと保護しながらの変更は難しい。

Mock酔い対策
戻り値が別の関数(Mock)の返す値と等しいテスト
→みればわかるので省略する

条件を満たしたときMockが呼び出されてその戻り値を返す。
→条件を満たさないとき別の値を返すテストがあれば省略、

引数に渡した値が別の関数に渡されるテスト
→省略

引数に渡したオブジェクトのあるプロパティの値が別の関数に渡されるテスト
→省略

引数に渡したコレクションのデータの内条件を満たすものが別の関数に渡されるテスト
→多分必要

フラグを見て対応した関数を呼び出すテスト
→フラグ自体も関数の引数にして、関数内で分岐するように修正して呼び出すテストは省略
→呼び出される関数の方ででテストする。

カテゴリー: ユニットテスト | タグ: , , , , , , , , , , , | コメントをどうぞ

RepositoryパターンでDBに接続せずににEntity Framework (Linq to Entity)によるデータの取得をテストする。

下記のRepositoryBaseから派生してRepositoryクラスを作成
_tableプロパティからLinqをかけば、
通常はLinq to Entityでクエリ構築、検索が行われ
SetTableData()に普通の配列などを渡せば、Linq to Objectで検索が行われるので
データベースに入ってる想定のデータを渡しててやれば、
実際にはデータベースに接続せずにRepositoryのテストを行うことが出来る。
また、DBに入っている値を入力としてテストが出来るので
Repositoryの検索関数をMockにして、検索関数が返す値を入力として扱う場合より広範囲を保護することが出来る。

    public class RepositoryBase<TContext,TEntity>
        where TContext:DbContext,new()
        where TEntity : class
    {
        DbContext context;

        protected IEnumerable<TEntity> _tabledata;
        protected IQueryable<TEntity> _table { get { return _tabledata.AsQueryable(); } }
        protected IDbSet<TEntity> _dbset { get { return _tabledata as IDbSet<TEntity>; } }

        public void SetTableData(IEnumerable<TEntity> data)
        {
            _tabledata = data;
        }

        protected RepositoryBase()
        {
            context = new TContext();
            SetTableData(context.Set<TEntity>());
        
        }
    }
カテゴリー: ユニットテスト, C#, C#-その他 | タグ: , , , , , | コメントをどうぞ

はじめてのユニットテスト /テスト苦悩開発

今参加中ASP.NET MVCのプロジェクト
成果物としてユニットテストが必要で、
本格的にユニットテストを書く初めてのプロジェクトなので。
勉強会や参考書などで聞きかじった
「テスト駆動開発(TDD)」とか「テストファースト」、「保護してリファクタリング」などを試してみようと
と張り切っていたのだが、
既存のテストコードをみると
ControllerのテストでControllerから普通にWorkerService,Model,Repositoryと順に呼び出してDBにアクセスしていて
WorkerServiceのテストでもModelのテストでも同じ様にそれ以下のクラスを順番に呼び出してDBにアクセスし,同じようなテストを何度も繰り返していた

自分も最初はそれに合わせてユニットテストの様なものを書いていたのだが、
全然単体テストではないし、それに
何かの本で単体テストではDBやファイルシステムにアクセスしてはいけないと読んだ気がする。
このままでは駄目だと思い ちゃんとテストを書くことにした

しかし、初めてのユニットテストなのでどう書けばいいのかよくわからない
そこで、とりあえず テスト対象の関数以外全てをMockにして,
適切なMock関数を適切な引数で呼び出していることを保証するテストを書いてみることにした。

作るのは、ドロップダウンでテーブルを選択し、
そのテーブルとその他の関連テーブルからEntity Frameworkでデータを取得し、
それを元に計算、最終的にFileStreamResultとして返すという物なのだが、

ControllerからWorkerService,Modelまではあまり問題はなかったように思う
引数を元にテーブルに対応した計算関数のMockを呼び出し、Mockの返した値ををStream作成関数のMockに渡してしてその戻り値にファイル名を付けてFileStreamResultとして返すといった形だが
一度形がきまってしまえば修正する必要がほとんどない

しかし、Modelや、Repositoryに入ると問題が出てきた
リファクタリングをしようと思い、ソースコードを少しでも変更すると
Mock生成部分を修正する必要が出てくるのだ
引数を増やせば引数が足りないとコンパイルエラーがでるし
呼び出す関数を変えればMockが適切に呼ばれていないというエラーが出る
修正には新しく呼ぶ方の関数のMockを用意して、それを呼び出していることをチェックする必要がある。
「保護してリファクタリング」をするはずが、少しでも修正しようとするとテストコードを直す必要があるのではリファクタリングのしようがない。
これではDBにアクセスしていて実行に時間がかかるし、単体テストではないがきちんと保護されていてリファクタリングの出来る既存コードの方がまだましである。

次に、Mockを呼び出したこと自体のテストはせずに、Mockが想定通りの呼び出し方で呼び出された時に最終的に返されるであろう値でテストを行うようにしてみた、Mock関数の返すはずの値を別の関数が返してもテストは通るが、その別の関数のMockが必要なことには変わりはなく、あまり効果はなかった

どうすればいいのかわからずに悩んでいたのだが、
EnittyFrameworkの生成するDbContextの派生クラスの持つテーブルに対応したDBSetプロパティにRepositoryで直接アクセスせずに、
一度IEnumerableのプロパティに保存してAsQueryable()でIQueryableに変換してLinqにつなぐようにすれば、そのIEnumerableプロパティに普通の配列やListをわたすことで、
Repositoryの検索関数のテストをDBに接続せずに行うことができることに気付いた、
(※AsQueryableを忘れるとそのテーブルの全データを取得してからLinq to Objectで検索してしまうので注意)

この方法を利用すればDBにアクセスせずにDBにこういう値が入っているときにModelがこういう値を返すというテストが可能になり、途中で呼び出す関数などにも一切制限はかからない、
DBにアクセスせずに「保護してリファクタリング」が可能になる!
と思ったのだが、冷静に考えるとModelのテストでRepositoryにアクセスしてしまっている。
DBにアクセスしていないことを除いては最初の状態と同じな気がしてきた……

そうして、また苦悩している時に或る言葉をふと思い出した
「TDDのテストと品質保証のテストは違う」
もしかしてこれか?、これでいいのか?
この基礎中の基礎をわかっていなかったことがすべての苦悩の原因なのか?

いろいろ調べてみた結果、
多少の誤解はありそうなものの、基本的にはその考えは正しそうであるという結論に達した。


[動画で解説]和田卓人の“テスト駆動開発”講座
第8回 テスト駆動開発の「サイクル」――まず受け入れテストで土台を作る

まずDBやファイルシステムにアクセスしてしまっても、
複数のクラスや関数を横断してしまいそうでもいいので、
保護、リファクタリングのできるテストを書けばいいのだ
それから中で呼び出す処理や関数を実装し、
必要であればMockを使った単体テストも用意すればよい。
最初からあったテストコードはそのような物は存在していないのに、
一度 納品・受領されているらしいのでおそらく必須ではないだろう。
保護可能テストさえ通っていればリファクタリング中はMockによる単体テストはコメントアウトしても
大丈夫なはずだ。

とりあえずこれでテスト苦悩開発から解放されそうな気がする。

カテゴリー: ユニットテスト, C#, C#-その他 | タグ: , , , , , , , , , | コメントをどうぞ

MSDNマガジン 年間総目次 2014

MSDN マガジン 2014

  1. MSDN マガジン January 2014
  2.  

  3. MSDN マガジン February 2014
  4.  

  5. MSDN マガジン March 2014
  6.  

  7. MSDN マガジン April 2014
  8.  

  9. MSDN マガジン May 2014
  10.  

  11. MSDN マガジン June 2014
  12.  

  13. MSDN マガジン July 2014
  14.  

  15. MSDN マガジン August 2014
  16.  

  17. MSDN マガジン September 2014
  18.  

  19. MSDN マガジン October 2014
  20.  

  21. MSDN マガジン November 2014
  22.  

  23. MSDN マガジン December 2014
  24.  

カテゴリー: MSDNマガジン | タグ: , | コメントをどうぞ

資料公開: わんくまT93 COM入門

2014/12/06 わんくま同盟東京勉強会 #93 で
「COM入門」 というタイトルでセッションを行いました

資料&サンプルコード
http://1drv.ms/1v3MecF

セッションで話し忘れてたこと等

  • GUIDはIID,CLSIDを表現するために生まれた
  • インターフェイスの実態は一定の条件を満たしたメモリブロック
    C++の継承じゃなくても条件を満たした方法であれば実装可能
    (純粋抽象クラスからの派生=仮想関数テーブルとほぼ同じ構造)
カテゴリー: その他, C++, COM, 勉強会 | タグ: , , , | コメントをどうぞ

COMメモ12 COMライブラリの初期化

初期化 CoInitialize(void* reserved);
終了 CoUninitialize();

カテゴリー: その他, C++, COM | タグ: , , , , | コメントをどうぞ

COMメモ11 DLLに必要な関数

  • STDAPI DllGetClassObject(婚stCLSID& clsid, const IiD& iid, void **ppv)
    • CLSIDを引数にとりクラスファクトリへのインターフェイスを返す。
  • STDAPI DllCanUnloadNow()
    • DLLをアンロードできる状態かを問い合わせる。
    • コンポーネントのコンストラクタ、デストラクタで参照カウントの操作を行いコンポーネントが使用されていないときのみUnloadできるようにする。
  • STDAPI DllRegisterServer()
    • COM登録用のコマンド? “REGSERVER32.EXE DLL名” 実行時に呼び出される
    • レジストリ関係のAPIを使ってコンポーネントをレジストリに登録するコードを記述する。
  • STDAPI DllUnregisterServer()
    • COM登録解除用コマンド “REGSERVER32.EXE /u DLL名” で呼び出される
    • レジストリからCOMコンポーネントを削除するコードを記述する
カテゴリー: その他, C++, COM | タグ: , , , , , , , | コメントをどうぞ