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
- [1] http://barkingiguana.com/2010/04/11/a-one-line-web-server-in-ruby/
- [2] https://gist.github.com/willurd/5720255#comment-855952