There's an echo in my head

日々のメモ。

TCPServerを相手にスレッドを使ったテスト

ok_gntpdというTCPServerをつかったサーバを相手にしたテストを書いた時のメモ。

テストしたい内容としては

  1. サーバを立ちあげて、
  2. 特定の文字列を送信したときに
  3. 特定の文字列を受信する

ということ。規模的に結合テストというほどのものではないけど、サーバを外から叩いた結果をチェックしたいのでサーバは別スレッドで動作させることになる。

こんな感じ:

require "spec_helper"

describe OkGntpd do
  specify do

    #== 1. サーバを立ちあげて、

    # サーバのインスタンス作成
    server = OkGntpd.new(
      # 空いてるポートを探すためのおまじない
      :port => TCPServer.open('127.0.0.1', 0) { |s| s.addr[1] }
    )
    # サーバの起動を子スレッドで
    t = Thread.new(server) do |s|
      Thread.pass # 親スレッドにすぐ引き渡す
      s.start     # サーバを起動開始(ただし親スレッドへの復帰とはズレるので、joinして待つ)
    end
    # サーバが起動するまで0.05秒間隔で親スレッドを止めてチェック
    t.join(0.05) until server.started?

    begin

      #== 2. 特定の文字列を書き込んだときに

      # ソケットを開いて書き込む
      sock = TCPSocket.open("127.0.0.1", server.options[:port])
      sock.write <<EOS
GNTP/1.0 REGISTER NONE\r
Application-Name: ok_gntpd\r
\r
EOS

      #== 3. 特定の文字列を受信する

      # 書き込んだ結果を見てサーバがデータを送り返してくるので、それを読み込む
      response = sock.read
      response.should == <<EOS
GNTP/1.0 -OK NONE\r
\r
EOS
    ensure
      sock.close
      t.exit # スレッドのクローズは忘れずに
    end
  end
end

テストを書くためにわざわざ追加したメソッドがあって、それはOkGntpd#started?というもの。別スレッドでサーバを動かすけども、それが起動したかどうか外から調べるために追加した。

テスト書くのは本体の実装よりも力量を求められる気がする。これまでスレッドとかソケットとかまともに触ってこなかったので、この量でも大変だった…。

このブログに出てくるコードスニペッツは、引用あるいは断りがない限りMITライセンスです。