Rails port of testing environment

2020-04-08 13:09发布

问题:

I'd like to test the HTTP API of our Rails app using Faraday and RSpec. Faraday needs the host url + port. Unfortunately the port of the testing environment does always change. How do I access the current port programmatically in the spec?

回答1:

If using Capybara you can set the port in the spec_helper.rb like so:

Capybara.server_port = 1234

See also: https://github.com/jnicklas/capybara/pull/123



回答2:

There's probably more than one way to do this, but this is working for me right now:

The command

port = `lsof -p #{Process.pid} -ai TCP -as TCP:LISTEN -Fn | grep ^n | cut -c 4- | uniq`.strip

Note that you'll have to do this at some point after the app has loaded - i.e., you can't use this in your environment.rb or application.rb file.

The explanation

Basically what this command does is as follows:

  • lsof is the Unix command for LiSt Open Files
  • -p #{Process.pid} limits it to the current process (i.e., your test web server instance)
  • -ai TCP limits it to "files" of type TCP (i.e., open TCP ports). (Note: the -a is to make it AND the search with the pid one - the default, using just -i would OR the search)
  • -as TCP:LISTEN limits to just TCP ports that are being listened on (as opposed to any open port - like your app's connection to Postgres for example)
  • -Fn tells it to only output the "name" column, which in this case will be the IP/port that is being listened on

The output of that part by itself will be something like this:

p12345
n*:5001
n*:5001

The first line, starting with p is the process ID. There's no way to suppress this. The next 2 lines (not sure why it can output multiples, but we'll take care of it in a minute) are the "name" column (hence n), followed by the IP + port. In our case (and I imagine yours as well, in a test environment), the web server listens on all available local IPs, thus *. Then it tells us that the port is, in this case 5001.

Finally, we pipe it through... * grep ^n to eliminate the first line (the process id) * cut to say "cut from columns 4 on" - i.e., remove the n*: to return just the port, and * uniq to just get the one instance

(It will also have a trailing newline, thus the strip call.)

The usage

In my case, I'm using this in my Cucumber env.rb thusly, to reconfigure the URL options for ActiveMailer so my email links get generated properly as working links in test:

port = lsof -p #{Process.pid} -ai TCP -as TCP:LISTEN -Fn | grep ^n | cut -c 4- | uniq.strip

MyApp::Application.configure do config.action_mailer.default_url_options[:host] = "0.0.0.0:#{port}" end

No doubt you could do the same thing in a helper/config for Rspec as well.