This is my NotificationChannel
class NotificationChannel < ApplicationCable::Channel
def subscribed
stream_from "notification_user_#{user.id}"
end
def unsubscribed
stop_all_streams
end
end
How can I write test for this ActionCable channels
This is my Rspec
require 'rails_helper'
require_relative 'stubs/test_connection'
RSpec.describe NotificationChannel, type: :channel do
before do
#user = create(:user)
#connection = TestConnection.new(#user)
#channel = NotificationChannel.new #connection, {}
#action_cable = ActionCable.server
end
let(:data) do
{
"category" => "regular",
"region" => "us"
}
end
it 'notify user' do
#error is in below line
expect(#action_cable).to receive(:broadcast).with("notification_user_#{#user.id}")
#channel.perform_action(data)
end
end
when I run this spec it gives error
Wrong number of arguments. Expected 2, got 1
I used this link to write code for stub and this file.
Rails version - 5.0.0.1
Ruby version - 2.3.1
expect(#action_cable).to receive(:broadcast).with("notification_user_#{#user.id}")
Looking closely broadcast needs two parameters so
expect(#action_cable).to receive(:broadcast).with("notification_user_#{#user.id}", data)
I cant guess what is going on however one issue is
let(:data) do
{
"action" => 'action_name',
"category" => "regular",
"region" => "us"
}
end
You need an action for perform_action.
However you dont have any action defined in NotificationsChannel.
Otherwise you can try
NotificationChannel.broadcast_to("notification_user_#{#user.id}", data )
I am using RSpec (Rails 3.2) to test my controllers. I have a controller which also contains a fileupload (using CarrierWave), but I keep getting the error:
Failure/Error: "image" => fixture_file_upload(Rails.root.join('spec',
'fixtures', 'files', 'image.png'), 'image/png')
RuntimeError:
.../spec/fixtures/files/image.png file does not exist
In my controller I have defined the image upload like this:
def valid_attributes
{ "title" => "My own title",
"description" => "Something cool",
"image" => fixture_file_upload(Rails.root.join('spec', 'fixtures', 'files', 'image.png'), 'image/png')
}
end
I have of course checked that file exists, but might there be something else that I have overlooked?
Seems you have to use Rack::Test::UploadedFile.new rather than fixture_file_upload in Rails 3.2:
def valid_attributes
{ "title" => "My own title",
"description" => "Something cool",
"image" => Rack::Test::UploadedFile.new(Rails.root.join('spec', 'fixtures', 'files', 'image.png'), 'image/png')
}
end
See this SO question/answers: fixture_file_upload has {file} does not exist error
I am doing functional tests for my controllers with Rspec. I have set my default response format in my router to JSON, so every request without a suffix will return JSON.
Now in rspec, i get an error (406) when i try
get :index
I need to do
get :index, :format => :json
Now because i am primarily supporting JSON with my API, it is very redundant having to specify the JSON format for every request.
Can i somehow set it to default for all my GET requests? (or all requests)
before :each do
request.env["HTTP_ACCEPT"] = 'application/json'
end
Put this in spec/support:
require 'active_support/concern'
module DefaultParams
extend ActiveSupport::Concern
def process_with_default_params(action, parameters, session, flash, method)
process_without_default_params(action, default_params.merge(parameters || {}), session, flash, method)
end
included do
let(:default_params) { {} }
alias_method_chain :process, :default_params
end
end
RSpec.configure do |config|
config.include(DefaultParams, :type => :controller)
end
And then simply override default_params:
describe FooController do
let(:default_params) { {format: :json} }
...
end
The following works for me with rspec 3:
before :each do
request.headers["accept"] = 'application/json'
end
This sets HTTP_ACCEPT.
Here is a solution that
works for request specs,
works with Rails 5, and
does not involve private API of Rails (like process).
Here's the RSpec configuration:
module DefaultFormat
extend ActiveSupport::Concern
included do
let(:default_format) { 'application/json' }
prepend RequestHelpersCustomized
end
module RequestHelpersCustomized
l = lambda do |path, **kwarg|
kwarg[:headers] = {accept: default_format}.merge(kwarg[:headers] || {})
super(path, **kwarg)
end
%w(get post patch put delete).each do |method|
define_method(method, l)
end
end
end
RSpec.configure do |config|
config.include DefaultFormat, type: :request
end
Verified with
describe 'the response format', type: :request do
it 'can be overridden in request' do
get some_path, headers: {accept: 'text/plain'}
expect(response.content_type).to eq('text/plain')
end
context 'with default format set as HTML' do
let(:default_format) { 'text/html' }
it 'is HTML in the context' do
get some_path
expect(response.content_type).to eq('text/html')
end
end
end
FWIW, The RSpec configuration can be placed:
Directly in spec/spec_helper.rb. This is not suggested; the file will be loaded even when testing library methods in lib/.
Directly in spec/rails_helper.rb.
(my favorite) In spec/support/default_format.rb, and be loaded explicitly in spec/rails_helper.rb with
require 'support/default_format'
In spec/support, and be loaded by
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
which loads all the files in spec/support.
This solution is inspired by knoopx's answer. His solution doesn't work for request specs, and alias_method_chain has been deprecated in favor of Module#prepend.
In RSpec 3, you need make JSON tests be request specs in order to have the views render. Here is what I use:
# spec/requests/companies_spec.rb
require 'rails_helper'
RSpec.describe "Companies", :type => :request do
let(:valid_session) { {} }
describe "JSON" do
it "serves multiple companies as JSON" do
FactoryGirl.create_list(:company, 3)
get 'companies', { :format => :json }, valid_session
expect(response.status).to be(200)
expect(JSON.parse(response.body).length).to eq(3)
end
it "serves JSON with correct name field" do
company = FactoryGirl.create(:company, name: "Jane Doe")
get 'companies/' + company.to_param, { :format => :json }, valid_session
expect(response.status).to be(200)
expect(JSON.parse(response.body)['name']).to eq("Jane Doe")
end
end
end
As for setting the format on all tests, I like the approach from this other answer: https://stackoverflow.com/a/14623960/1935918
Perhaps you could add the first answer into spec/spec_helper or spec/rails_helper with this:
config.before(:each) do
request.env["HTTP_ACCEPT"] = 'application/json' if defined? request
end
if in model test (or any not exist request methods context), this code just ignore.
it worked with rspec 3.1.7 and rails 4.1.0
it should be worked with all rails 4 version generally speaking.
Running Rails 5 and Rspec 3.5 I had to set the headers to accomplish this.
post '/users', {'body' => 'params'}, {'ACCEPT' => 'application/json'}
Thi matches what the example in the docs looks like:
require "rails_helper"
RSpec.describe "Widget management", :type => :request do
it "creates a Widget" do
headers = {
"ACCEPT" => "application/json", # This is what Rails 4 accepts
"HTTP_ACCEPT" => "application/json" # This is what Rails 3 accepts
}
post "/widgets", { :widget => {:name => "My Widget"} }, headers
expect(response.content_type).to eq("application/json")
expect(response).to have_http_status(:created)
end
end
Per the Rspec docs, the supported method is through the headers:
require "rails_helper"
RSpec.describe "Widget management", :type => :request do
it "creates a Widget" do
headers = {
"ACCEPT" => "application/json", # This is what Rails 4 and 5 accepts
"HTTP_ACCEPT" => "application/json", # This is what Rails 3 accepts
}
post "/widgets", :params => { :widget => {:name => "My Widget"} }, :headers => headers
expect(response.content_type).to eq("application/json")
expect(response).to have_http_status(:created)
end
end
For those folks who work with request tests the easiest way I found is to override #process method in ActionDispatch::Integration::Session and set default as parameter to :json like this:
module DefaultAsForProcess
def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: :json)
super
end
end
ActionDispatch::Integration::Session.prepend(DefaultAsForProcess)
Not sure if this will work for this specific case. But what I needed in particular was to be able to pass a params hash to the post method. Most solutions seem to be for rspec 3 and up, and mention adding a 3rd parameter like so:
post '/post_path', params: params_hash, :format => 'json'
(or similar, the :format => 'json' bit varies)
But none of those worked. The controller would receive a hash like: {params: => { ... }}, with the unwanted params: key.
What did work (with rails 3 and rspec 2) was:
post '/post_path', params_hash.merge({:format => 'json'})
Also check this related post, where I got the solution from: Using Rspec, how do I test the JSON format of my controller in Rails 3.0.11?
Why don't RSpec's methods, "get", "post", "put", "delete" work in a controller spec in a gem (or outside Rails)?
Based off this question, you could try redefining process() in ActionController::TestCase from https://github.com/rails/rails/blob/32395899d7c97f69b508b7d7f9b7711f28586679/actionpack/lib/action_controller/test_case.rb.
Here is my workaround though.
describe FooController do
let(:defaults) { {format: :json} }
context 'GET index' do
let(:params) { defaults }
before :each do
get :index, params
end
# ...
end
context 'POST create' do
let(:params) { defaults.merge({ name: 'bar' }) }
before :each do
post :create, params
end
# ...
end
end
I'm just starting out with RSpec and having a little difficulty writing up controller tests for nested resources. I've tried googling this, but without much luck.
Could someone offer a basic example of a "PUT update" test test ensures a nested resource is updated? Just to elaborate, I have the equivalent (non-nested) resource tested like this:
def mock_post(stubs={})
#mock_post ||= mock_model(Post, stubs).as_null_object
end
...
describe "PUT update" do
describe "with valid parameters" do
it "updates the requested post" do
Post.stub(:find).with("14") { mock_post }
mock_post.should_receive(:update_attributes).with({'these' => 'params'})
put :update, :id => "14", :post => {'these' => 'params'}
end
end
end
I've been trying for some time to correctly stub a similar test for a 'Comment' model which is nested under Post, but no joy. Any suggestions appreciated.
You'll need to have both id's passed to your put method
put :update, :id => "14", :post_id=> "1", :comment => {'these' => 'params'}
I'm using ActiveScaffold with Ruby on Rails and I'm loving it, however there is one weird thing. Whenever I hit "Edit" or "Create New" in my webapp's ActiveScaffold, it says "Create {{model}}" or "Update {{model}}" in the webapp rather than using the model's name. Why is this? I have an ads_controller.rb that includes this:
active_scaffold :ad do |config|
config.label = "Ads"
config.columns = [:name, :description, :imageUrl, :linkUrl, :apps, :created_at, :updated_at]
config.update.columns = [:name, :description, :imageUrl, :linkUrl, :apps]
config.create.columns = config.update.columns
list.sorting = {:created_at => 'DESC'}
columns[:imageUrl].label = "Image URL"
columns[:linkUrl].label = "Link URL"
end
And my routes.rb includes this:
map.namespace :admin do |admin|
admin.root :controller => 'admin_home', :action => 'index'
admin.resources :ads, :active_scaffold => true
end
Any thoughts on why I'm seeing "Create {{model}}" instead of "Create ad" ?
I just had this problem yesterday.
Looks like the correct syntax to perform interpolation on resources is using %{model} instead of {{model}}, despite some documentation claiming otherwise.
Check your resource files in config/locales.
If you have activescaffold installed as a plugin, you'll have to edit:
vendor/plugins/active_scaffold/lib/active_scaffold/locale/en.yml
and replace the {{model}}, etc with %{model}