RSpecでテストケースごとにHTTPサーバーを立てて試験する

puppeteer-rubyを開発していて、いよいよ単体試験を書くことにした。

そこで問題となったのが、「ボタンだけを含むHTMLページを開く」みたいな試験。

本家Puppeteerでは静的なHTMLページをいくつか用意してHTTPサーバーを立ててmocha(JavaScriptのテスト)を流しているっぽいのだけど、単体試験としてそれはどうなの?

Rubyだったら適当なDSLを駆使して

describe 'click button' do
  http_server do
    get '/button.html' do
      <<~HTML
      <html>
        <head><title>button</title></head>
        <body>
          <button onclick='document.body.innerHTML="123"'>click here</button>"
        </body>
      </html>
    end
  end

  it 'can click button' do
    page.goto('/button.html')
    page.click('button')
    expect(page.body).to eq("123")
  end
end

こんな感じで必要最低限のHTMLを返すHTTPサーバーを都度立てるspecを書けたらいいのに?と思った。

Webmockはtoo much

RSpecでモックHTTPサーバーを作る方法をぐぐると、webmock が出てくる。

ただ、こいつは

github.com

このへんのソースを見ると、HTTPリクエストがされたかどうかを確認するマッチャなども定義されているし、今回はそもそもHTTP通信をモック化したいわけではなくて、テストサーバーを立てたいだけなのだから、やりたいことに対してあまりにもtoo muchだ。

SinatraDSL

やりたいことの

  http_server do
    get '/button.html' do
      <<~HTML
      <html>
        <head><title>button</title></head>
        <body>
          <button onclick='document.body.innerHTML="123"'>click here</button>"
        </body>
      </html>
    end

これに近いことができるのは、RubyだとSinatraだ。

Sinatra::Baseのソースコードを読んでみたところ、

app = Sinatra.new do
  get '/hello' do
    'Hello World'
  end
end

app.run!

...

app.quit!

こんな感じで、HTTPサーバーが非常に簡単に立てることができる。

Sinatraを立てるDSLを定義する

さて、RSpecで冒頭に書いたようなspecを書けるようにするには、どうすればいいだろう?

とりあえずhttp_serverっていうメソッドをExampleGroupに生やしてやればいいのではないか。

module TestHttpServer
  def http_server(port: 8000, &block)
    require 'net/http'
    require 'sinatra/base'

    app = Sinatra.new(&block)

    around do |example|
      Thread.new { app.run!(port: port) }

      # wait until server is started.
      loop do
        Net::HTTP.get(URI("http://127.0.0.1:#{port}/"))
        break
      rescue Errno::ECONNREFUSED
        sleep 0.1
      end

      example.run

      app.quit!
    end
  end
end

RSpec::Core::ExampleGroup.extend(TestHttpServer)

spec_helperを上記のようにしてみたら、うまくいった。

 

これで puppeteer-rubyテスト駆動開発できるようになったぞ〜〜