Index: [Article Count Order] [Thread]

Date:  Thu, 19 Feb 2004 03:29:33 +0900 (JST)
From:  Susumu YAMAZAKI <yamazaki@....jp>
Subject:  [XP-jp:04917] マルチスレッドプログラムの単体テスト
To:  extremeprogramming-jp@....jp
Message-Id:  <20040219.032933.41430381.yamazaki@....jp>
X-Mail-Count: 04917

山崎です.

スレッド間の通信機構の単体テストをどう書くか, 知恵を貸してください.

例えば下記のようなメッセージキューを実装するとします. 基本的には, 二つ
スレッドがあって, 一方が send, もう一方が receive を行う使い方をすると
します. そして, send が S1, S2, receive が R1, R2 という処理で構成され
ているとします.

public class MessageQueue {
    public void send(Object message) { S1; S2; }
    public Object receive() { R1; R2; }
}

テストしたい内容は, 例えばスレッド1(以下 T1 と書きます)が S1 を実行し,
S2 を実行する直前に, 別のスレッド T2 が R1 を実行した直後, ブロックす
ることを確認する, といった感じです.

僕が思いついた方法を説明します. まず S1 と S2, R1 と R2 の間にフックと
なるメソッド呼び出しをあらかじめ定義しておきます.

public class MessageQueue {
    public void send(Object message) { S1; sendHook(); S2; }
    public Object receive() { R1; receiveHook(); R2; }
    protected void sendHook() {}
    protected void receiveHook() {}
}

ちょっと長いですが, テストプログラムはこんな感じ

public class MessageQueueTest extends TestCase {
    public MessageQueueTest(String arg0) {
        super(arg0);
    }

    class MessageQueueSub1 extends MessageQueue {
        volatile boolean flag = false;
        final Object breakPoint = new Object();
        protected void hookSend() {
            synchronized(breakPoint) {
                try {
                    breakPoint.wait();
                } catch(InterruptedException e) {}
            }
        }
        protected void hookReceive() {
            flag = true;
        }
    }

    class ThreadSub1 extends Thread {
        public Object result;
        public MessageQueue queue;
        public ThreadSub1(MessageQueue queue) {
            this.queue = queue;
        }
        public void run() {
            result = queue.receive();
        }
    }

    public void testSub1() {
        final MessageQueueSub1 queue = new MessageQueueSub1();
        final Object value = new Object();
        Thread t1 = new Thread() {
            public void run() {
                queue.send(value);
            }
        };
        ThreadSub1 t2 = new ThreadSub1(queue);
        t1.start();
        t2.start();
        Thread.yield();
        assertFalse(queue.flag);
    }
}

MessageQueueSub1 はフックを定義していて, send の時は停止して receive 
の時には通過したというフラグを立てます. ThreadSub1 は receive の返値を
格納しておくスレッドです. testSub1 がテストメソッド本体で, queue を用
意して t1, t2 を走らせて, t2 がフラグを立てないことを確認します.

この方法で気に入らない点は以下の通りです.

1. あらかじめフックを入れるのがいまいち.

2. フラグで間接的に調べるだけでなく, t2 が synchronized, wait 等で停止
   しているのかどうかを確認したい.

何かいいアイデアがあったら, よろしくお願いします.

                  山崎 進 --- yamazaki@....jp
                   福岡県産業・科学技術振興財団
                     福岡知的クラスター研究所