just starting to learn RSpec and TDD, and can't figure out why it's don't work at all.
#spec/api/event_api_spec.rb
describe 'Messages API' do
it 'check response' do
get 'api.mydomain.dev/events'
json = JSON.parse(response.body)
# test for the 200 status-code
expect(response).to be_success
end
end
I have create my API on api.mydomain.dev and my folder structure looks like app/controllers/api/events_controller.rb
So when I tried to run bundle exec rspec it's shown that
NoMethodError:
undefined method `get' for #<RSpec::ExampleGroups::MessagesAPI:0x007fc34900cee0>
if I'm trying to make smth like Event.creat!(:name => 'My Event') in My Spec file #spec/api/event_api_spec.rb it says
NameError:
uninitialized constant Event
So i don't understand How require my app/controllers/api/events_controller.rb file to the Spec file to get instance of my Event Class to get it work.
With default controllers it's work fine, I only interesting in API setup, thx
"get" is a method available on controller specs, try with something like
describe 'Messages API', type: :controller do
to tell rspec you are testing something like a controller, or maybe just do
describe EventsController do
About the "uninitialized constant Event", try with this at the begning of spec/api/event_api_spec.rb
require 'rails_helper'
Hope this helps, but your post is a little confusing, is not clear if your api is online or what, you shouldn't test agains the online api, you should test locally, specs should never communicate with the real world unless it's really really necessary.
Related
I'm writing an integration test for Rails v5.1 using built-in Minitest.
Here's the integration test class:
require 'test_helper'
class PuppiesEndpointsTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
test "DELETE puppy" do
marty = people(:marty)
sign_in(marty)
# delete puppies_delete_path(marty.puppies.first.id)
# delete `/api/v1/puppies/destroy/${marty.puppies.first.id}.json`
# delete puppies_path(marty.puppies.first.id)
delete '/api/v1/puppies/destroy/6666.json'
assert_response :success
end
end
All of the routes above, including the ones that are commented out, result in the same cryptic error:
Error:
PuppiesEndpointsTest#test_DELETE_puppy:
NoMethodError: undefined method `[]=' for nil:NilClass
test/integration/puppies_endpoints_test.rb:17:in `block in <class:PuppiesEndpointsTest>'
bin/rails test test/integration/puppies_endpoints_test.rb:7
It doesn't give a stack trace or any other information to diagnose what the hell it's talking about. I used byebug to debug the marty variable right before that delete line that's throwing the error. It shows the expected puppies array of associated (fixture) records.
I've also placed a byebug at the very top of the controller action and this error fails the test before it reaches that byebug, so I think that pretty much rules out anything in the action code.
Here's the relevant chunk of what I see when I run rake routes:
PATCH /api/v1/puppies/edit/:id(.:format) puppies#update
DELETE /api/v1/puppies/destroy/:id(.:format) puppies#destroy
puppies_create POST /api/v1/puppies/create(.:format) puppies#create
Here's what is actually in the my routes file:
scope '/api' do
scope '/v1' do
devise_for :people
patch 'puppies/edit/:id' => 'puppies#update'
delete 'puppies/destroy/:id' => 'puppies#destroy'#, as: 'puppies_delete'
post 'puppies/create' => 'puppies#create'
...
I'm completely stumped as to what/why I'm getting this error. The actual code is working completely as expected.
My hunch is that maybe there's a missing config variable that's not getting set for the test environment (I use dotenv gem), but I have no idea how to track that down if the error won't give me any context whatsoever.
UPDATE
I have isolated this problem to using the Devise helper sign_in method. When I remove this method call, the problem goes away.
Here's the problematic test class:
require 'test_helper'
class PuppiesEndpointsTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
test "do stuff" do
...
app/controllers/api_controller.rb:
class ApiController < ActionController::API
end
Maybe sign_in does not work for testing controllers that do not inherit from ActionController::Base
I changed the controller to inherit from ActionController::Base and nothing changed. I still can't use sign_in without getting that error, but it works find if I "manually" post a request to the sign_in endpoint.
UPDATE 2
I found this Devise issue which sounds related to my problem: https://github.com/plataformatec/devise/issues/2065
Looks like I found the issue. Apparently, in rails-api mode, the ActionDispatch::Cookies and ActionDispatch::Session::CookieStore middlewares are inserted in the end of the middleware stack, which doesn't occur in normal Rails mode.
Due to this, those middlewares are included after Warden::Manager which messes up something in request specs.
Try to set in test.rb
Rails.application.config.middleware.insert_before Warden::Manager, ActionDispatch::Cookies
Rails.application.config.middleware.insert_before Warden::Manager, ActionDispatch::Session::CookieStore
Typically I avoid controller tests and stick to Model & Cucumber, but i find myself needing one and I cant seem to get moving.
The following code is simplistic.. i have ripped out just about everything
1 require 'spec_helper'
2
3 describe ExperiencesController do
4 describe "GET #show" do
5 context "experience item" do
6 it "redirects to the success url" do
7 experience = build(:experience)
8 get :show, :id => experience.id
9 #should be a test here :)
10 end
11 end
12 end
13 end
Yields the following
Failures:
1) ExperiencesController GET #show experience item redirects to the success url
Failure/Error: get :show, :id => experience.id
NoMethodError:
undefined method `id' for Sinatra::Application:Class
# (__DELEGATE__):2:in `get'
# ./spec/controller_spec/experiences_controller_spec.rb:8:in `block (4 levels) in <top (required)>'
$ rake routes|grep experience
experiences GET /experiences(.:format) experiences#index
POST /experiences(.:format) experiences#create
new_experience GET /experiences/new(.:format) experiences#new
edit_experience GET /experiences/:id/edit(.:format) experiences#edit
experience GET /experiences/:id(.:format) experiences#show
PUT /experiences/:id(.:format) experiences#update
DELETE /experiences/:id(.:format) experiences#destroy
Really feels like a config type of thing
I dont understand why it would be yielding a Sinatra error
Any help appreciated
** Update **
SideKiq and its dependency sinatra is installed in the Gemfile for this rails app.
And I believe that sinatra may be interfering with controller test.
Ok.. so the problem was a collision of mistakes
1.) Because Im using Sidekiq I had the following in my config route
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'
That awesome piece of code mounts a Sinatra based web interface to monitor Sidekiq
2.) If you notice above my directory above is wrong
It was incorrectly spec/controller_spec instead of spec/controller
When that happens rspec doesnt know you are intending to test controllers and doesnt
load all the Get/post helpers , instead in the loaded stack the only thing it
found was sinatra which has a get method..
Solution ..
Move the spec to the proper directory so it properly infers the right dependencies
IT10T error..
I am trying to test a helper in a Rails application. I am new at rspec and am having a hard time getting the test to run. Here is my test file:
require 'spec_helper'
describe "ServiceHoursHelpers" do
include ServiceHoursHelper
describe "test" do
it "should equal Jason" do
test.should eql("Test")
end
end
end
And here is my test file:
module ServiceHoursHelper
def test
"Test"
end
end
Here is the command I am running
rspec spec/helpers/service_hours_helper_spec.rb
It isnt succeeding and there are no errors, so obviously I am not hitting the code. What is it that I am missing?
Try run your test method like this:
helper.test.should eql("Test")
After digging fairly deeply on this issue, I've come to an impasse between my understanding of the documentation and my results.
According to https://www.relishapp.com/rspec/rspec-rails/v/2-8/docs/routing-specs/route-to-matcher, we should be able to write the following:
#rspec-rails (2.8.1)
#rspec (>= 1.3.1)
#rspec-core (~> 2.8.0)
# routing spec
require "spec_helper"
describe BusinessUsersController do
describe "routing" do
it "routes to some external url" do
get("/business_users/7/external_url").should route_to("http://www.google.com")
end
end
end
# routes.rb
BizeebeeBilling::Application.routes.draw do
resources :business_users do
member do
get "external_url" => redirect("http://www.google.com")
end
end
end
Running this spec produces the following results:
Failures:
1) BusinessUsersController routing routes to some external url
Failure/Error: assert_routing "/business_users/7/external_url", "http://www.google.com"
ActionController::RoutingError:
No route matches "/business_users/7/external_url"
# ./spec/routing/business_users_routing_spec.rb:19:in `block (3 levels) in <top (required)>'
I have not been able to find anyone reporting this specific issue anywhere.
Added detail: the route is resolved perfectly well when testing manually.
Routing specs/tests specialize in testing whether a route maps to a specific controller and action (and maybe some parameters too).
I dug into the internals of Rails and Journey a bit. RSpec and Rails (basically, some details left out) use Rails.application.routes.recognize_path to answer the question "is this routable?"
For example:
$ rails console
> Rails.application.routes.recognize_path("/business_users/1", method: "GET")
=> {:action=>"show", :controller=>"business_users", :id=>"1"}
However, there's no controller on the other end of /business_users/1/external_url. In fact, to perform the redirect, Rails has created an instance of ActionDispatch::Routing::Redirect, which is a small Rack application. No Rails controller is ever touched. You're basically mounting another Rack application to perform the redirection.
To test the redirect, I recommend using a request spec instead (a file in spec/requests). Something like:
require "spec_helper"
describe "external redirection" do
it "redirects to google.com" do
get "/business_users/1/external_url"
response.should redirect_to("http://www.google.com")
end
end
This tests the route implicitly, and allows you to test against the redirection.
Andy Lindeman has the correct answer. However, you don't have to put the spec in spec/requests, you can keep it in spec/routing and be explicit with the metadata "type": describe 'my route', type: :request do
I was running into a similar case where I was trying to test a series of routes, some which should redirect and some which shouldn't. I wanted to keep them in a single routing spec, since that was the most logical way to group them.
I tried using describe: 'my route', type: request, but found that not to work. However, you can include RSpec::Rails::RequestExampleGroup in your spec context to gain access to the request spec methods. Something like:
describe "My Routes" do
context "Don't Redirect" do
it "gets URL that doesn't redirect" do
get("business_users/internal_url").should route_to(controller: "business_users", action: "internal_url_action")
end
end
context "Redirection" do
include RSpec::Rails::RequestExampleGroup
it "redirects to google.com" do
get "/business_users/1/external_url"
response.should redirect_to("http://www.google.com")
end
end
end
The simplest way to test external redirects is to use an integration test:
test "GET /my_page redirects Google" do
get "/my_page"
assert_redirected_to "https://google.com"
end
You test needs to be under your test/integration directory or the equivalent directory where the integration tests should go.
I think you want the redirect_to matcher.
I'd like to put a few integration tests in a separate directory from my controller unit specs. However, when I move my spec file to spec/integration, it fails with:
ArgumentError:
bad argument(expected URI object or URI string)
The spec passes correctly when in the spec/controllers directory.
Here's a bit from my spec:
require 'spec_helper'
describe Users::LoginsController, type: :controller do
let!(:user) { User.create(email: 'test#test.com', password: 'test')
it 'logs in the user' do
post :create, email: 'test#test.com', password: 'test'
controller.current_user.should == user
end
end
I'm using Rails 3.1.3, RSpec 2.7.0.
Are there any tricks I have to use to achieve this?
Try specifying type:
describe ProductsController, type: :controller do
it 'can test #show' do
get :show
end
end
Works in Rails 3.2.11
You have to do the following:
describe Users::LoginsController do
controller_name 'users/logins'
... the rest of your spec here ...
end
I am not entirely certain about the nested syntax, but at least you need to specify the controller_name to get it to work.
Hope this helps.
The test framework does not like it if you specify the test action using a symbol.
it 'logs in the user' do
post :create, email: 'test#test.com', password: 'test'
controller.current_user.should == user
end
becomes
it 'logs in the user' do
post 'create', email: 'test#test.com', password: 'test'
controller.current_user.should == user
end
I had the same problem and ended up simply using the URL
get "/users"
It's not as clean but gets the job done. I couldn't get the other suggestions to work.
This approach works for me in Rails 4 and RSpec 2.14.7:
My findings:
Don't name your directory integration. I need to grep through the source of either RSpec or Rails, but it appears to have an adverse effect on running controller specs. Perhaps someone with this knowledge can chime in to confirm this.
Name it something other than integration; in my case I tried int, which resulted in problems until I added the "type: :controller" after the #describe method.
After that, I was able to move all of my slow Rails specs into my int directory, allowing me to create a unit directory for all of my decoupled, fast specs.
Please let me know if this works for you.
By the way, I am running:
Ruby 1.9.3
Rails 4.0.2
rspec-core 2.14.7
rspec-rails 2.14.1
all on Mac OS X.
Take the opportunity now to get rid of your controller and integration specs. They're needlessly painful to write and too coupled to implementation. Replace them with Cucumber stories. Your life will be much easier...