Advertisement

Some of my rubygems – e.g. proxy_pac_rb – either need a backing HTTP(S)-server for their tests or interact with some external web services via HTTP/HTTPS. To mock external services you can either use VCR, webmock or just plain Webrick. To serve data for your tests via HTTP(S), you can use Webrick as well. As Webrick is part of ruby core and is sufficient for both use cases, this article only describes ways to run a Webrick-HTTP(S)-server to back your test suite.

Servers

Preparations

Please create a script-directory in your project.

mkdir -p script

Afterwards, please install the webrick-gem.

gem install webrick

Simple HTTP-server returning a “String”

The code given in this section …

  • … listens on TCP port “8000”
  • … returns “Example Domain Cleartext” on each request
  • … uses plain HTTP without any encryption

Create a file named script/http_server.rb with the following content.

#!/usr/bin/env ruby

require 'webrick'

server = WEBrick::HTTPServer.new(
    :Port => 8000,
)

server.mount_proc '/' do |req, res|
  res.body = 'Example Domain Cleartext'
end

server.start

After that, make the script an executable.

chmod +x script/http_server.rb

To start the server, run ./script/http_server.rb

# Start HTTP server
./script/http_server.rb
# => [2015-08-02 07:19:17] INFO  WEBrick 1.3.1
# => [2015-08-02 07:19:17] INFO  ruby 2.2.2 (2015-04-13) [x86_64-linux]
# => [2015-08-02 07:19:17] INFO  WEBrick::HTTPServer#start: pid=15600 port=8000

To check if the server is running correctly, invoke curl – you may need to install this program on your machine.

curl http://localhost:8000
# => Example Domain Cleartext

Don’t forget to kill the server afterwards. Otherwise you might see some errors, that another server cannot bind itself to TCP port “8000”.

Simple HTTPS-server returning a “String”

The code given in this section …

  • … listens on TCP port “8443”
  • … returns “Example Domain Encrypted” on each request
  • … uses TLS/SSL to encrypt the traffic with a self-signed certificate

Create a file named script/https_server.rb with the following content.

#!/usr/bin/env ruby

require 'webrick'
require 'webrick/https'

cert_name = [
  %w[CN localhost],
]

server = WEBrick::HTTPServer.new(
    :Port => 8443,
    :SSLEnable => true,
    :SSLCertName => cert_name
)

server.mount_proc '/' do |req, res|
  res.body = 'Example Domain Encrypted'
end

server.start

A certificate has a “Distinguished Name” which should match the URL for the website. The code given below uses CN localhost for this.

cert_name = [
  %w[CN localhost],
]

This is the most interesting part of the code given above. In this section you enable SSL and define the “Distinguished Name” for the certificate.

server = WEBrick::HTTPServer.new(
    :Port => 8443,
    :SSLEnable => true,
    :SSLCertName => cert_name
)

After that, make the script an executable.

chmod +x script/https_server.rb

To start the server, run ./script/https_server.rb

# Start HTTP server
./script/https_server.rb
# => [2015-08-02 07:19:17] INFO  WEBrick 1.3.1
# => [2015-08-02 07:19:17] INFO  ruby 2.2.2 (2015-04-13) [x86_64-linux]
# => [2015-08-02 07:19:17] INFO  WEBrick::HTTPServer#start: pid=15600 port=8443

To check if the server is running correctly, invoke curl again.

curl -k https://localhost:8443
# => Example Domain Encrypted

And again: Don’t forget to kill the server afterwards. Otherwise you might see some errors, that another server cannot bind itself to TCP port “8443”.

Serve local directory via HTTP

The code given in this section …

  • … listens on TCP port “8000”
  • … serves files from the current working directory
  • … uses plain HTTP without any encryption

If you want to serve a local directory via HTTP, you can also do this with Webrick. Just pass it :DocumentRoot with Dir.pwd [1].

# Ruby < 1.9.3
ruby -r webrick -e 'WEBrick::HTTPServer.new(:Port => 8000, :DocumentRoot => Dir.pwd).start'
# => [2015-08-02 07:19:17] INFO  WEBrick 1.3.1
# => [2015-08-02 07:19:17] INFO  ruby 2.2.2 (2015-04-13) [x86_64-linux]
# => [2015-08-02 07:19:17] INFO  WEBrick::HTTPServer#start: pid=15600 port=8000

# Ruby >= 1.9.3
ruby -r webrick -e 'WEBrick::HTTPServer.new(Port: 8000, DocumentRoot: Dir.pwd).start'
# => [2015-08-02 07:19:17] INFO  WEBrick 1.3.1
# => [2015-08-02 07:19:17] INFO  ruby 2.2.2 (2015-04-13) [x86_64-linux]
# => [2015-08-02 07:19:17] INFO  WEBrick::HTTPServer#start: pid=15600 port=8000

There’s also a library called “un” which is part of ruby core, uses Webrick behind the scenes and makes the line above much easier [2].

ruby -r un -e httpd . -p 8000
# => [2015-08-02 07:19:17] INFO  WEBrick 1.3.1
# => [2015-08-02 07:19:17] INFO  ruby 2.2.2 (2015-04-13) [x86_64-linux]
# => [2015-08-02 07:19:17] INFO  WEBrick::HTTPServer#start: pid=15600 port=8000

Integration

RSpec

To integrate your HTTP-server into your RSpec-test suite, you can use the following piece of code – for HTTPS just replace the executable in the #spawn-call with script/https_server.rb. You may need to adjust the value of the #sleep-call to your needs. Without the #sleep-call, the test will fail because the HTTP-server needs some time to start up.

require 'open-uri'

RSpec.describe 'Run server' do
  before :all do
    @web_server = Process.spawn(
      'script/http_server.rb',
      in: :close,
      out: 'tmp/httpd-out.log',
      err: 'tmp/httpd-err.log'
    )

    sleep 1
  end

  before :each do
    @content = open('http://localhost:8000').read
  end

  it { expect(@content).to eq 'Example Domain Cleartext' }
  it { expect(@content).to match /Cleartext/ }
  it { expect(@content).to include 'Cleartext' }

  after :all do
    Process.kill 'TERM', @web_server
  end
end

The following lines spawn the script/http_server.rb, write all output on STDOUT to tmp/httpd-out.log and all output on STDERR to tmp/http-err.log and wait 1 sec before moving on.

before :all do
  @web_server = Process.spawn(
    'script/http_server.rb',
    in: :close,
    out: 'tmp/httpd-out.log',
    err: 'tmp/httpd-err.log'
  )

  sleep 1
end

The lines find below download content from the HTTP-server and check the result of the download.

require 'open-uri'

before :each do
  @content = open('http://localhost:8000').read
end

it { expect(@content).to eq 'Example Domain Cleartext' }
it { expect(@content).to match /Cleartext/ }
it { expect(@content).to include 'Cleartext' }

After all tests have run, the HTTP-server is terminated.

after :all do
  Process.kill 'TERM', @web_server
end

Rake

You may want to integrate the HTTP-server into your Rakefile instead – maybe to use the same HTTP-server for your RSpec- and Cucumber-tests. To use an HTTPS-server replace the executable in the #spawn-call with script/https_server.rb.

# Set default task to test
task default: :test

namespace :test do
  desc 'Setup test environment'
  task :before do
    $stderr.puts 'Starting server'

    @web_server = Process.spawn(
      'script/http_server.rb',
      in: :close,
      out: 'tmp/httpd-out.log',
      err: 'tmp/httpd-err.log'
    )

    sleep 1
  end

  desc 'Teardown test environment'
  task :after do
    $stderr.puts 'Stopping server'

    Process.kill 'TERM', @web_server
  end
end

desc 'Run test suite'
task :test do
  Rake::Task['test:before'].invoke

  begin
    # Change this to your needs
    %w(test:rubocop test:rspec test:cucumber).each { |t| Rake::Task[t].invoke }
  ensure
    Rake::Task['test:after'].invoke
  end
end

namespace :test do
  desc 'Run cucumber test suite'
  task :cucumber do
    sh 'bundle exec cucumber'
  end

  desc 'Run rspec test suite'
  task :rspec do
    sh 'bundle exec rspec'
  end

  desc 'Run rubocop'
  task :rubocop do
    sh 'bundle exec rubocop'
  end
end

If you start rake without any argument it should run the test-task.

task default: :test

The following lines will run the test:before-, the test:rubocop-, the test:rspec-, the test:cucumber- and the test:after-tasks – in the given order.

desc 'Run test suite'
task :test do
  Rake::Task['test:before'].invoke

  begin
    # Change this to your needs
    %w(test:rubocop test:rspec test:cucumber).each { |t| Rake::Task[t].invoke }
  ensure
    Rake::Task['test:after'].invoke
  end
end

The following lines spawn the script/http_server.rb, write all output on STDOUT to tmp/httpd-out.log and all output on STDERR to tmp/http-err.log and wait 1 sec before moving on.

task :before do
  @web_server = Process.spawn(
    'script/http_server.rb',
    in: :close,
    out: 'tmp/httpd-out.log',
    err: 'tmp/httpd-err.log'
  )

  sleep 1
end

The last task is going to terminate the HTTP-server.

desc 'Teardown test environment'
task :after do
  Process.kill 'TERM', @web_server
end

Conclusion

It’s quite easy to setup an HTTP(S)-server with Ruby using Webrick. It can be customized to your needs and requires no other rubygem installed on your machine. If you need to setup an HTTP-proxy for your tests as well, you may want to read the following article about using Webrick as HTTP-proxy.

Try it yourself! Happy Browsing! Thanks for reading!

References

Discussion

If you found a mistake in this article or would like to contribute some content to it, please file an issue in this Git Repository

Disclaimer

The contents of this article are put together to the best of the authors' knowledge, but it cannot be guaranteed that it's always accurate in any environment. It is up to the reader to make sure that all information found in this article, does not do any damage to the reader's working environment or wherever this information is applied to. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, arising from, out of or in connection with this article. Please also note the information given on the Licences' page.