Friedrich Ewald My Personal Website

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 a files 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 the test_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
As the port number is dynamic, the HTTP calls also need to be constructed dynamically. I found that it is relatively easy to write <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
Besides the installation of the webrick gem, that is all that is needed to serve static files during tests in Rails from the fixtures/files folder.


About the author

is an experienced Software Engineer with a Master's degree in Computer Science. He started this website in late 2015, mostly as a digital business card. He is interested in Go, Python, Ruby, SQL- and NoSQL-databases, machine learning and AI and is experienced in building scalable, distributed systems and micro-services at multiple larger and smaller companies.