Ruby3 ユニットテストのレビュー

Ruby3は読んでいて、ストーリーがしっかりしているから面白い。
この項目の流れも面白かった。


四則演算の簡単なプログラムを書いて、それをテストしていくお話。テストは始め自作する。すると、同じようなメソッドを何回も書くことになり、前章で勉強したフレームワークを使うことになる。この話の流れが上手いなと思った。同じようなメソッドを書いているので、フレームワークがとてもありがたい。

  • 利用するフレームワークRuby標準添付のMiniTestというテスティングフレームワーク
  • テスト用クラスは、Test::Unit::TestCaseクラスを継承する
  • テストメソッドは、メソッド名の頭にtestとつける
  • Test::Unit::Testcase > MiniTest::Unit::Testcase
  • テストメソッドの実行方法
   begin
     setup()
     method()
   rescue
   ensure
     teardown()
   end
  • テスト結果の検証・失敗時の表示には表明メソッドを使用

ユニットテストの勉強

Ruby3 第6章を読んでみたので、覚書。

ユニットテスト

  • プログラムを開発するためのツール
  • 開発したプログラムの単体機能検証
    • ある1つのプログラム・ライブラリ・クラスが仕様を満たしているか


ユニットテストが持つ「ツール」と「単体機能の検証」の2つの意味に混乱した。
要するに、パーツをテストしながら作成して、最後に全体の動作をテストするってことでしょうか。

ユニットテストプログラムとは

ユニットテストについて詳しく説明されてたのでメモ

  • プログラムが正しく動作しているか確かめるプログラム
  • プログラムと同時・先行して作っていく
  • テストを書きやすくするために、プログラムは読みやすいようにする

テストの種類

ユニットテストの他にもテストについて少し書かれてたのでメモ。
挙げられたテストを見ていると、開発の工程でテストする人が違うことが面白かった。
テストが必要なのはプログラマだけでなく、依頼者まで必要なんだ。
考えてみたら、依頼したものと全然違うものが出来上がったら困るもんな。

  • テスト名(別名):説明[テストする人]

簡単なwebサーバをプログラムで書いてみた

「ruby3 5章1 簡単なWebサーバーのプログラミング」を読んだのでレビュー

目次

  1. ソケットの利用
  2. HTTPリクエストの内容
  3. HTTPレスポンスの内容
  4. Webサーバの仕組み
ソケットの利用

ソケットの説明が書いてある章。
Rubyでソケットを利用する場合はソケットクラスを使う。TCPSoketクラス・TCPServerクラスなどが紹介されている。

HTTPリクエストの内容

HTTPリクエストの説明が簡単だけど要点が抑えられてて解りやすかった。HTTPリクエストの構造やURLエンコードなどの説明がされている。

HTTPリクエス
HTTPで規定されたGETリクエストをサーバーに向けて送信
HTTPレスポンスの内容

HTTPレスポンス構造の説明がある。読んでて面白かったのは、HTTPの主なステータスコート・カテゴリ・メタデータを簡単に紹介しているところ。200・301・404・503など身近なステータスコードが説明されている。

Webサーバーの仕組み

HTTPリクエスト・HTTPレスポンスの動作確認をしたところでWebサーバーの仕組みについて説明。TCPSocketとTCPServerを説明した図があるのだけれど、よくわからなかった。クライアントとサーバーの動きを図示しているが、動きの説明がなく図を見て理解するしかない。ここも説明あると嬉しい・・・

プログラミング

フォームに文字を入力し、その文字が表示される簡単なwebサーバを書いた。

  1 # encoding: cp932
  2 
  3 require "socket"
  4 require "uri"
  5 
  6 def header(data)
  7   map = {}
  8   data.scan(/([^:]+):\s*(.+)\s*\r\n/) do |k, v|
  9     map[k] = v
 10   end
 11   map
 12 end
 13 
 14 TCPServer.open(8000) do |server|
 15   loop do
 16     sock = server.accept()
 17     data = ""
 18     begin
 19       data << sock.read(1)
 20     end while !data.index("\r\n\r\n")
 21     reqline = data[0...data.index("\r\n")]
 22     metadata = header(data[reqline.size + 2..-3])
 23     body = nil
 24     if metadata["Content-Length"]
 25       body = sock.read(metadata["Content-Length"].to_i)
 26       name = URI.decode(body.match(/name=(.*)/)[1])
 27       break if name == "end"
 28     end
 29     resp = "<html><body>"
 30     if body
 31       resp << "<div>hello #{name} !</div>"
 32     end
 33     resp << "<form name='form' method='post'>"
 34     resp << "<input type='text' name='name'><input type='submit'></form>"
 35     resp << "</body></html>"
 36     sock.write("HTTP/1.1 200 OK\r\n")
 37     sock.write("Content-Type: text/html; charset=Windows-31J\r\n")
 38     sock.write("Content-Length: #{resp.size}\r\n")
 39     sock.write("Connection: close\r\n")
 40     sock.write("\r\n")
 41     sock.write(resp)
 42     sock.close()
 43   end
 44 end

これを改造してファイル名を指定し、内容を上に表示するプログラムに書き換えてみた。下記のopen_request_fileメソッドを追加。if metadata["Content-Length"]〜endを書き変え

def open_request_file(name)
  f = open(name, "r")
  content = ""
  f.each do |line|
    content << "#{line}<br>"
  end
  content
end
 24     if metadata["Content-Length"]
 25       body = sock.read(metadata["Content-Length"].to_i)
 26       name = URI.decode(body.match(/name=(.*)/)[1])
 27       break if name == "end"
 28     end
 29

  # 下記のように書き換え

if metadata["Content-Length"]
  body = sock.read(metadata["Content-Length"].to_i)
  file_name = URI.decode(body.match(/file_name=(.*)/)[1])
  req_file = open_request_file(file_name)
  break if file_name.nil?
end

RubyでクWebServerができたので、Tokyo Cabinetのクライアントみないなもの作ってみたい

shiftの罠

Array#shiftメソッドの仕様を気にせずに使っていたので、ハマった事を書きます。

配列をshiftしていって、空配列になった時。配列aは全ての要素が取り出された時、a = nilになると思っていました。
しかし、shiftは配列の要素を取り出すメソッドなので配列自身は消えないんですよね。よくよく考えれば気づくことなんだけど、今後注意しなければ。

irb(main):001:0> a = [1,2]
=> [1, 2]
irb(main):002:0> a.shift
=> 1
irb(main):003:0> a.shift
=> 2
irb(main):004:0> a.shift
=> nil
irb(main):005:0> a.nil?
=> false
irb(main):006:0> a.empty?
=> true

nil?メソッドでtrueが返ってくるとおもってたのですが、配列自身は空配列になっている。

ブロック付きメソッドとyield制御構造

ブロック引数について書いた記事があったのですが、間違ってたのでその記事は消しました。
ごめんなさい。

まずは、ブロックについて、勉強やり直します。

ブロック
doからend 又は、{} で囲われた一連の式
class Each_test
  def initialize(a)
    @myarr = a
  end

  def each
    max = @myarr.size - 1
    for i in (0..max) do
      yield(@myarr[i])
    end    
  end
end

test = Each_test.new([0, 1, 2])
test.each{|value| puts value}

この場合、{|value| puts value}がブロック。このブロックはeachメソッドの引数なので、ブロック引数という。

  • Each_test#eachを実行
    • 初期化の際に指定された配列@myarr = ["no", "no", "war"]が対象。
  • yield(@myarr[i])より、@myarr[i]を引数にしたブロック{|value| puts value}を実行。
    • value = @myarr[i]
    • yieldの意味は「譲る」

私が読んでる参考書に「yield制御構造をメソッドの中で呼び出すと、そのメソッドにも与えられたブロックを実行する。」と書いてありました。この「ブロックを実行する」という動きがどうしても想像できなかった。ブロック引数というので、流れとして

  1. {|value| puts value}を#eachへ渡す
  2. 渡されたブロックを受け取る引数がない!!!!

と考えて大混乱してました。

テンプレートメソッドパターン

  • 基底クラス
    • 共通の処理を実装する
    • 派生クラス独自の処理をからのメソッドとして定義しておく
  • 派生クラス
    • 基底クラスの空メソッドをオーバーライドする

※上記2つのメソッドはprivateをかけて、外部に公開しないようにする