八葉の日記

日々、感じたことをまとめる場として利用する

Humble Object

Humble Object at XUnitPatterns.com
非同期プログラミング - 非同期コードの単体テスト: テストを容易にする 3 つの解決策


Humble Object はインスタンス化することが難しいオブジェクトを、効率的にテストするための考え方です。(詳細は上記リンクを参照デス)
例えば、
・ビジュアルコンポーネントウィジェット、ダイアログなど)があるフレームワーク上にある場合です。
 この時はUnitTestのためには、フレームワークで依存している全オブジェクトをUnitTest環境で構築する必要があります
・非同期に実行されるためテストが難しいアクティブオブジェクトです。
 ※アクティブオブジェクト:スレッド、プロセス、ウェブサーバなどのタスクの基点(エントリーポイント)となる関数を所有するクラスのオブジェクト

使い方
①すべてのロジックをテスト対象のコンポーネントから同期テストを介してテスト可能なコンポーネントに抽出します。
②①のコンポーネントは、テストが難しいコンポーネントのロジックを、動機かつパブリックで呼び出しできるサービスインターフェイスを実装します。
③その結果、Humble Object トコンポーネントは非常に薄いアダプタレイヤーになり、コードはほとんど含まれません。
④ハンブルオブジェクトがフレームワークによって呼び出されるたびに、それはテスト可能なコンポーネントに委譲されます。

例えば、アクティブオブジェクトRequestHandlerThread が初期化されることをテストしようとする場合、
下記をターゲットにして、runして数秒後にinitializedSuccessfullyがtrueを返すことを確認するようなコードを書くのですが、
processOneRequest内部に複雑な処理があって、initializedSuccessfullyの結果が設定される場合は安定したUnitTestを書くことができない。
(実際に数秒まったり、フレームワークの状態によってはエラーとなる)

public class RequestHandlerThread extends Thread {
   private boolean _initializationCompleted = false;
   private int _numberOfRequests = 0;
  
   public void run()  {
      initializeThread();
      processRequestsForever();
   }
  
   public boolean initializedSuccessfully() {
      return _initializationCompleted;
   }
  
   void processRequestsForever() {
      Request request = nextMessage();
      do {
         Response response = processOneRequest(request);
         if (response != null) {
            putMsgOntoOutputQueue(response);
         }
         request = nextMessage();
      } while (request != null);
   }
}

この例では簡単であるが、ストラテジーパターンを使用して、requestHandlerに処理を移譲している。
こうすると、requestHandlerを同期で試験する、スレッドに対しては処理が移譲できていることを確認すればOKとなる。

public class HumbleRequestHandlerThread extends Thread
implements Runnable {
   public RequestHandler requestHandler;
  
   public HumbleRequestHandlerThread() {
      super();
      requestHandler = new RequestHandlerImpl();
   }
  
   public void run() {
      requestHandler.initializeThread();
      processRequestsForever();
   }
  
   public boolean initializedSuccessfully() {
      return requestHandler.initializedSuccessfully();
   }
  
   public void processRequestsForever() {
      Request request = nextMessage();
      do {
         Response response = requestHandler.processOneRequest(request);
         if (response != null) {
            putMsgOntoOutputQueue(response);
         }
         request = nextMessage();
      } while (request != null);
   }



コードは参考ページから持ってきています。
英語自信ないため解釈誤りありましたらごめんなさい^^