Rails testing with static server
I wanted to test some part of my application that performs HTTP requests against a different website. Now, for testing, I didn’t want to perform actual outside HTTP calls because they tend to become flaky if the internnet becomes unstable or when the remote resource is not available. Another reason, why I didn’t want to use the rails server and their static files, is that I didn’t want to serve those files in production. So I decided to write my own static file server.
Requirements
The requirements are quite simple. I wanted something that I can spin up from Ruby in the current process and that is able to serve static files from a predefined folder. For this purpose, I created afiles
folder under fixtures
. The other requirement is that I did not want it to clash with any other server that might be running on the target system. The reason is that I am planning to run this on my CI pipeline and I have very little control over what runs there. Furthermore, if tests run in parallel I didn’t want to spend much time on synchronization, instead spin up a temporary server for each test and shut it down immediately afterwards.
Solution
I came up with two methods that I added to thetest_helper.rb
in my Rails application: fixture_server
and replace_port!
. The former starts a server and returns the instance of the server itself, giving full control over the server process, including shutdown. This is intended if the server should run for a whole suite instead of for a single test. The block mode allows to start a server inline, and end it immediately. This mode is intended for cases when only a single tests uses the server. See the following example for example usage.
def demo_explicit_shutdown
server = fixture_server
port = server.config[:Port]
# Do something with the server
# ...
# Stop server
server.shutdown
end
def demo_block_mode
fixture_server do |server, port|
# Do something with server and port
# ...
# No need to shutdown server
end
end
<PORT>
in my URLs and then use the small helper method replace_port!
to replace the port via regular expression inline.
The complete code for the methods fixture_server
and replace_port!
is listed below. The dynamic port number is achieved by passing 0
. This tells the system to use the first available port. The server is started in a different thread to be non-blocking and automatically shuts down upon receiving a SIGINT
signal.
# Start a temporary server for fixtures which gets automatically shut down.
# The port number is returned
def fixture_server
root = Rails.root.join('test', 'fixtures', 'files')
server = WEBrick::HTTPServer.new :Port => 0, :DocumentRoot => root
trap 'INT' do server.shutdown end
Thread.new do
server.start
end
if block_given?
yield(server, server.config[:Port])
server.stop
end
server
end
# Replace port placeholder <PORT> with real port
def replace_port!(s, port)
s.gsub! /<PORT>/, port.to_s
end
webrick
gem, that is all that is needed to serve static files during tests in Rails from the fixtures/files
folder.