Serve Static File in Cucumber Test from an HTTP location - ruby-on-rails

I'm testing a JSON API for a model with a Carrierwave Uploader, but I'm only allowing uploads via a remote_url, so I need some way to get an HTTP address for a file in my public assets, since I know those'll always be around.
To put it another way:
When the Rails server is running, I have a static asset at "http://localhost:3000/assets/logo.png". I need the address of that file while Cucumber is running - aka, I need to serve a static file while running Cucumber, so that a different part of the Rails app can "download" that file.
Edit: These test are not run with a browser, although (as of recently), they are run with a session. I may be using Cucumber, but I have written no code to use or start Capybara.

If I'm understanding your question correctly. You may want to look into something like Fakeweb or Webmock to serve back/mock external requests.

I think you can get the root uri for the test server that cucumber starts by using these:
For capybara < 2.0:
Capybara.current_session.driver.rack_server.host
Capybara.current_session.driver.rack_server.port
Capybara 2.0:
Capybara.current_session.server.host
Capybara.current_session.server.port

Related

Rspec: run an outside rails server

This question is about starting a rails server of the external project from a rspec environment.
There is 2 projects.
First project act as the Admin Back Office, it's the central application where users interact with web pages. I call it BackOffice
Second project is a Json API Server which will receive commands from the Admin Back Office through json requests.I call it ApiServer
I am trying to test API interaction between those 2 rails projects, and I would like to set-up rspec so I can write and maintain my spec files in BackOffice project. Those specs would start a ApiServer rails server and then play around to perform the tests.
My issue is about starting the ApiServer rails server. After looking at the rails app initialization files, I assumed I had to add a require to "config/environment".
But when I insert into BackOffice/spec/spec_helper.rb
require File.expand_path('../../../ApiServer/config/environment', __FILE__)
I get the error
`initialize!': Application has been already initialized. (RuntimeError)
# Backtrace to the file:
# ApiServer/config/environment.rb
# Line:
# Rails.application.initialize!
I also tried to simply call the following in backticks
`cd /api/path; bundle exec rails s -p 3002`
but got the same kind of error
Then I got inspiration from Capybara source code, and required the "ApiServer/application", then I am able to create a ApiServer.new object, but as soon as I call initialize! on it it I get the same message.
Any help is greatly appreciated. Cheers
Actually the second app is nothing more then an external service, which is better to stub for the tests.
There is one nice article from thoughtbot about using vcr gem to mock external web services:
https://robots.thoughtbot.com/how-to-stub-external-services-in-tests
Obligatory "don't do that unless you really need to".
However, since it seems you know what you need:
Short answer:
You need to isolate both application in system environment and launch it from there using system-calls syntax.
Long answer:
What you're trying to do is to run two Rails applications in the same environment. Since they both are Rails applications they share a lot of common names. Running them ends in name clash, which you're experiencing. Your hunch to try simple back ticks was good one, unfortunately you went with a bundler in already existing environment, which also clashes.
What you have to do in order to make it work is to properly isolate (in terms of code, not in terms of network i.e. communication layer ) application and then run launcher from rspec. There are multiple ways, you could:
Use Ruby process control (Check this graph, you could try to combine it with system level exec)
Daemonize from Operating System level (init.d etc.)
Encapsulate in VM or one of the wrappers (Virtualbox, Vagrant, etc.)
Go crazy and put code on separate machine and control it remotely (Puppet, Ansible, etc.)
Once there, you can simply run launcher (e.g. daemon init script or spawn new process in isolated environment) from RSpec and that's it.
Choosing which way to go with is highly dependent on your environment.
Do you run OSX, Linux, Windows? Are you using Docker? Do you manage Ruby libraries through things like RVM? Things like this.
Generally it's a bad idea to require launching another service/application to get your unit tests to pass. This type of interaction is usually tested by mocking or vcring responses, or by creating environment tests that run against deployed servers. Launching another server is outside the scope of rspec and generally, as you've discovered, will cause a lot of headaches to setup and maintain.
However, if you're going to have these rails projects tightly coupled and you want them to share resources, I'd suggest investigating Rails Engines. To do this will require a substantial amount of work but the benefits can be quite high as the code will share a repository and have access to each other's capabilities, while maintaining application isolation.
Engines effectively create a rails application within another rails application. Each application has it's own namespace and a few isolating guards in place to prevent cross app contamination. If you have many engines it becomes ideal to have a shell rails application with minimal capabilities serving each engine on a different route/namespace.
First you need to create housing for the new api engine.
$ rails plugin new apiserver --mountable
This will provide you with lib/apiserver/engine.rb as well as all the other scaffolding you'll need to run your API as an engine. You'll also notice that config/routes.rb now has a route for your engine. You can copy your existing routes into this to provide a route path for your engine. All of your existing models will need to be moved into the namespace and you'll need to migrate any associated tables to the new naming convention. You'll also have some custom changes depending on your application and what you need to copy over to the engine, however the rails guide walks your through these changes (I won't enumerate all of them here).
It took a coworker about a week of work to get a complicated engine copied into another complicated rails server while development on both apps was occurring and with preserving version control history. A simpler app -- like an api only service -- I imagine would be quicker to establish.
What this gives you is another namespace scope at the application root. You can change this configuration around as you add more engines and shared code to match various other directory structures that make more sense.
app
models
...
apiserver
app
...
And once you've moved your code into the engine, you can test against your engine routers:
require "rails_helper"
describe APIServer::UsersController do
routes { APIServer::Engine.routes }
it "routes to the list of all users" do
expect(:get => users_path).
to route_to(:controller => "apiserver/users", :action => "index")
end
end
You should be able to mix and match routes from both services and get cross-application testing done without launching a separate Rails app and without requiring an integration environment for your specs to pass.
Task rabbit has a great blog on how to enginize a rails application as a reference. They dive into the what to-do's and what not-to-do's in enginizing and go into more depth than can be easily transcribed to a SO post. I'd suggest following their procedure for engine decision making, though it's certainly not required to successfully enginize your api server.
You can stub requests like:
stub_request(:get, %r{^#{ENV.fetch("BASE_URL")}/assets/email-.+\.css$})

RSpec/Capybara: Cross-Domain JS Testing

I'm working on an advertising application, where there is some cross-domain JS. I'd like to write tests that verify that the cross-domain JS is working as intended.
My first thoughts are that I would need to be able to
visit some_url_thats_not_my_rails_app
However, Capybara throws a "No Route Matches", since it obviously goes to only relative paths, and is intended for testing your OWN website. But I really need to go on ANOTHER page, and verify that things like the serialized token are identical.
Is Capybara the right tool for this? If so, what do I need to do to force non-relative paths?
Yes, Capybara is right tool for it. You can also use Ruby with Selenium webdriver(Would be awesome with page object gem) or Ruby with Watir webdriver.
To visit and test any web application, you can use Ruby and Capybara. For this you need to set app_host. Add this one in support/env.rb:
Capybara.app_host = "http://flipkart.com"
And in hooks.rb
Before do
visit('/')
end
You can use Capybara with Selenium webdriver and you will be good to test any deployed application in production, staging or prep env.

Rails, Cucumber, Capybara, Selenium, Pow, domains, subdomains and javascript

TL:DR - How can I make Cucumber request a page through the app but pretend that the request came from "http://mysubdomain.mydomain.dev", and run the javascript properly?
Ok, this may take a while to explain, but please bear with me...
I'm a 'relatively' experienced Rails dev, but only recently took the plunge into full BDD/TDD.
The page I'm testing in cucumber has some javascript in it for creating a new nested object (very similar to this RailsCast).
Now the problem lies in the fact that not only do I have variable sub-domains, I have variable Domains too (Just trust me on that one...)
So the app needs to query the request.host to find the current_domain and the current_subdomain before everything ties up in the background and it can carry on serving the right bits of the app.
I've managed to get all of the tests passing fine using the host! domain and Capybara.default_host = domain techniques in the background steps.
However, when I use the #javascript tag on the feature that tests a page with .js on it, I get Firefox grabbing focus, then trying to load the full url.
Now it also happens that I'm running Pow, and have those urls hooked up to the dev server. Unsurprisingly, they don't work as when the user tries to log in, it's looking in the dev DB. I tried running the suite after turning the pow server off, and it just timed out.
Surely the javascript webdriver shouldn't be actually accessing the url, but simply running the app itself and pretending that the host is what I tell it to be???
I'm obviously missing something here - How can I make Cucumber build the page internally, but pretend that the request came from "http://mysubdomain.mydomain.dev"?
edit: Jason - the variable domain trick is achieved exactly the same as subdomains... If you can query the db for an account based on request.subdomains.first, you can query for a domain through request.domain. You just have to double check a few things like capitalisation etc. to minimize the risk of a malformed url breaking stuff, and obviously you need to ensure that the domain record exists in the db first...
Oh - and cache the domain record requests with care...
It means that you can serve the same app, but with different styling and landing pages etc. Useful for PaaS apps with a broad customer base - you can re-brand it and sell it as a specific solution to one group's problems, even though it's the same guts underneath.
I am looking to do something very similar. I thought about adding an additional pow directory for test, then using the pow directives to override the environment. I think this is done within a ".powenv" inside your app directory. Here is an example of a quick fix:
echo export RAILS_ENV=cucumber > .powenv && touch tmp/restart.txt
But it would be better to do something dynamic so that before you start the test, you can tell pow what env to run, then when finished switch back, maybe even run the test server on a different port on a temp basis. Pow is the only great solution I know of so far for handeling subdomains.
EDITED: I have this working now in my environment with the following addition to my features/support/env.rb file.
# Switch Pow to For Cucumber Tests
Capybara.default_driver = :selenium # Subdomain testing will only work with pow and selenium
pow_config = "#{Rails.root}/.powenv" # Dont change, this is the Config Files Location.
pow_config_stash = "#{Rails.root}/.powenv_original" # This is what the config will be stashed as during testing.
Before do
# Set the default host
Capybara.app_host = "http://www.resipsa.dev"
# Stash the existing config
File.rename(pow_config,pow_config_stash) if File.exists? pow_config
# Write the new pow config
f = File.new("#{Rails.root}/.powenv", "w")
f.write "export RAILS_ENV=test"
f.close
# Touch tmp/restart.txt to force a restart
FileUtils.touch "#{Rails.root}/tmp/restart.txt"
end
After do
# Delete the temp config
File.delete(pow_config)
# Restore the Original Config
File.rename(pow_config_stash,pow_config) if File.exists? pow_config_stash
# Touch tmp/restart.txt to force a restart
FileUtils.touch "#{Rails.root}/tmp/restart.txt"
end

Running selenium/watir inside the context of the rails server

How can I run a watir test in the context of the app that's being tested? I'd like my test to browse the app and then access ActionMailer::Base.deliveries for emails or check models directly. This is how I understand what's being described here.
UPDATE: They probably use Capybara to be able to acces the email array and be in the context of the "server" which is instantiated just for the test.
I suggest checking out the Rails unit testing docs, then writing a simple Rails test case that starts your app - then try adding a line or two of Watir code to access your app:
http://guides.rubyonrails.org/testing.html
As far as I know you should be able to write a Rails unit test, then put Watir code inside one of your test methods - and if all goes well you should be able to instantiate your web app, use Watir to launch a browser to test it, and in the same method(s) perform non-Watir low-level testing (e.g. checking models/data/etc.)
I've never used Watir inside a Rails test, but I don't see why it wouldn't work.
Watir is about driving browsers to automate functional testing. You could I suppose use it for unit testing of the top level UI stuff, but more often in a 'unit test' context that would be done using a headless browser emulation, Capybara, celerity, or watir-webdriver using the headless option, purely for speed of operation since driving an actual browser can be slow even with a fast browser like chrome.
Most of the times people use Watir it's for more functional tests, often from a test runner framework like Cucumber, sometimes Rspec depending on your needs. You might combine that with other ruby code to access or create test data, to validate something made it into the DB from the UI, but everything in the Watir gem is all about the browser and interacting with it much like a human would, and driving the browser is it's function within the set of tools you might use.
I had the same need and found the following solution: https://stackoverflow.com/a/9687562/90741. I reproduce it here, as the linked question seems to be dead, with its owner...
I had the same need (Watir+RSpec), but the problem is: there is no Rack stack running by default during tests...
I found this solution:
In spec_helper.rb:
HTTP_PORT = 4000
test_instance_pid = fork do
exec 'unicorn_rails -E test -p %d' % HTTP_PORT
end
at_exit do
Process.kill "INT", test_instance_pid
Process.wait
end
Which start the test stack once for all spec tests, and kill it at the end. (In this sample, I am using unicorn, but we can imagine using any other server)
In the spec, I reuse the HTTP_PORT constant to build URL:
browser.goto "http://localhost:#{HTTP_PORT}/"

Testing Sinatra Applications inside Rails

I've created a simple application to assist me in learning Rails.
Because I like a challenge, I've also started to build, inside this application, a Sinatra app (to handle API calls without the overhead of the full Rails stack).
I've come to the point where I want to start testing both applications before I move any further, but I'm not sure where to write the tests for my Sinatra app. It's presently located under lib/api - should I create a tests folder underneath that, or use the main Rails test folder at the root?
You can test this sinatra app in request of rspec by example or by integration test.
In this part you just need define which url you want request and see the result.

Resources