Testing ActionMailer with both paramaters and arguments - ruby-on-rails

How do you test a mailer method that uses both params and arguments.
class UserMailer < ActionMailer::Base
def invoice(invoice)
#user = params[:user]
...
end
end
None of these assertions will work:
assert_enqueued_email_with UserMailer.with(user), :invoice, args: { invoice }
assert_enqueued_email_with UserMailer.with(user), :invoice, args: [ user, invoice ]
assert_enqueued_email_with UserMailer.with(user), :invoice, params: { user: user }, args: [ invoice ]

This has been updated on Rails edge with this PR. You can now use the assertion as follows:
assert_enqueued_email_with UserMailer.with(user), :invoice, args: [ user, invoice ]

You can check enqueued job
It's not so elegant like assert_enqueued_email_with but this method can't access parameters as arguments
UserMailer.with(user: user).invoice(invoice).deliver_later
job_args = ActiveJob::Base.queue_adapter.enqueued_jobs[0][:args]
assert_equal job_args.first, "UserMailer"
assert_equal job_args.second, "invoice"
assert_equal job_args.fourth["params"]["user"], user
assert_equal job_args.fourth["args"], [invoice]
There is also assert matcher for enqueued job. And it's possible to check ActionMailer::MailDeliveryJob with array of 4 arguments and passing mailer delivery to the block
assert_enqueued_with(job: ActionMailer::MailDeliveryJob, args: ["UserMailer", "invoice", "deliver_now", { params: { user: user }, args: [invoice] }], queue: "default") do
UserMailer.with(user: user).invoice(invoice).deliver_later
end

Related

How can I test my API created stripe customer properly?

I want to test the API request that creates Stripe customers.
Controller
def create
user = User.create(create_params)
stripe_customer = Stripe::Customer.create({
email: create_params[:email],
name: [create_params[:first_name], create_params[:last_name]].join(' ')
})
user.update(stripe_customer_id: stripe_customer.id)
render(json: { user: user }, status: :ok)
end
private
def create_params
params.permit(
:email,
:first_name,
:last_name
)
end
I saw there is stripe-ruby-mock gem but I am not sure how I can use it?
First of all, you should be able to trust that Stipe's gem will do the right thing when you call it. So, there's no need to test that their create method returns the correct thing. So, all you need to test is that you are calling their API correctly. All you need to do this is an RSpec mock.
Since your code is in a controller, you'll either need to use a controller spec, or you'll need to refactor your code into another class. My preference is the refactor. So, that's what I'll show here.
Controller
Refactored to move business logic into a command class.
def create
user = CreateUser.new(create_params).perform
render(json: { user: user }, status: :ok)
end
CreateUser command class
Refactored a bit to only make one call to the database.
class CreateUser
attr_reader :params
def initialize(params)
#params = params
end
def perform
stripe_customer = Stripe::Customer.create({
email: params[:email],
name: [params[:first_name], params[:last_name]].join(' ')
})
User.create(params.merge(stripe_customer_id: stripe_customer.id))
end
end
Spec file for CreateUser command class
describe CreateUser do
subject(:create_user) { described_class.new(params)
let(:params) do
email: 'some#email.address',
first_name: 'first',
last_name: 'last'
end
describe 'perform' do
let(:stripe_customer_id) { 123 }
before do
allow(Stripe::Customer).to receive(:create).and_return(stripe_customer_id)
allow(User).to receive(:create)
end
it 'creates a Stripe customer' do
create_user.perform
expect(Stripe::Customer).to have_received(:create).with(
email: 'some#email.address',
name: 'first last'
)
end
it 'creates a user with a stripe customer id' do
create_user.perform
expect(User).to have_received(:create).with(
email: 'some#email.address',
first_name: 'first',
last_name: 'last',
stripe_customer_id: 123
)
end
end
end

RSpec controller test - ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash

In my Rails 6 app I'm trying to test controller methods which allow admin users to update user data without providing user passwords. All actions are run in ActiveAdmin.
admin/users.rb
controller do
def update
model = :user
%w[password password_confirmation].each { |p| params[model].delete(p) } if params[model][:password].blank?
super
end
end
Based on this page I tried to write specs:
admin/users_spec.rb
describe 'PUT update' do
let(:user) { create(:user, :random_email) }
let(:valid_attributes) do
ActionController::Parameters.new(
{
user: {
email: 'michael.kelso#example.com',
password: '',
},
},
)
end
before do
put :update, params: { id: user.id, user: valid_attributes }
end
it 'update user' do
expect(user.reload.email).to eq('michael.kelso#example.com')
end
end
end
What should I do to have this test green?
you can still add strong params for activeadmin.
Using method permit_params method like: permit_params :title, :content, :publisher_id
activeadmin strong param docs:
https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
this post is related to this one: https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters

Testing strong parameters with shoulda_matchers

I'm having a hard time trying to figure out how to test the strong parameters with rspec and shoulda_matchers.
No matter what I've tried, I always get the message: RSpec::Expectations::ExpectationNotMetError: Expected POST #create to restrict parameters on :language_name to :name,
but it did not restrict any parameters.
This is my controller:
class LanguageNamesController < CrudController
inherit_resources
actions :all, :except => [:show]
private
def language_name_params
params.require(:language_name).permit(:name)
end
end
This is the test:
require 'rails_helper'
RSpec.describe LanguageNamesController, type: :controller do
describe "parameters" do
it do
lm_params = {
language_name: {
name: 'John'
}
}
expect(subject).to permit(:name).for(:create, params: { params: lm_params }).on(:language_name)
end
end
end
Also, I've tried this and got the same result:
it do
subject.params = ActionController::Parameters.new(language_name: {foo: 'bar', name: 'baz'})
expect(subject).to permit(:name).for(:create).on(:language_name)
end
if I try like this, I got ArgumentError: unknown keyword: language_name:
it do
lm_params = {
language_name: {
name: 'John'
}
}
expect(subject).to permit(:name).for(:create, params: lm_params).on(:language_name)
end
I'm using:
ruby 2.5.1
rails 5.1.6
shoulda-matchers 3.1.2
Anyone know how to solve this?
Thank you!
This is my workaround to test it:
it do
faker_name = Faker::Name.name
params = {
language_name: {name: faker_name, other_attribute: "other value"},
extra: {extra: 1}
}
get :index, params: params
expect(subject.send(:language_name_params)).to eq({"name"=> faker_name})
end
You do not need to create lm_params. Shoulda matchers are going to mock up the params object for you.
If you only need to test the permissions you can do should permit(:name).for(:create)
If you need to check for a value on a permitted parameter: should permit(:name).for(:create, params: {name: 'John'})
There are good examples in the documentation for strong_parameters_matcher.rb
EDIT: Another good source of documentation is the rubydoc for shoulda matchers.

Unit testing rails controller, class not receiving method... Why?

Say I have a users controller with a separate CreateUser class that when called creates a user. I have CreateUser in my controller and want to make sure that it is being called on.
I'm getting an ActiveRecord error. Very confused because I'm stubbing out the CreateUser class so I'm not touching the database. Confused why CreateUser is not being called on.
What am I doing wrong here?
Controller spec:
# spec/controllers/users_controller_spec.rb
# ...
describe "create action" do
before do
# build fake data
created_user = FactoryGirl.build_stubbed(:user)
result = double(:context, user: created_user, success?: true)
# don't hit the database by stubbing creation process
allow(CreateUser).to receive(:call).and_return(result)
end
after do
post :create, user: { name: "bob" }
end
# this test fails
it "calls on CreateUser interactor" do
expect(CreateUser).to receive(:call)
end
# this test passes
it "renders something in json" do
expect(controller).to receive(:render)
end
end
Controller:
# app/controllers/users_controller.rb
class UsersController < ApplicationController
# ...
def create
result = CreateUser.call # why is this not being called?
render json: result.user, status: :created
end
private
def user_params
params.permit(:name)
end
end
Output:
Failure/Error: expect(CreateUser).to receive(:call)
(CreateUser (class)).call(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
The issue I was having was because of CanCan's load_and_authorize_resource. The fix is to stub out that method at top of RSpec test:
allow_any_instance_of(CanCan::ControllerResource).to receive(:load_resource)

Functional Testing for Customer Parameter Validation - Rspec / Capybara

I Added customer validation in my controller like
def customer_create
if params[:api_key].present?
## Create Customer
else
## Render Error Message in Json format
end
end
How can I write Rspec Testing for above method?
Thanks In Advance
presumably customer_create is called from your controller's create action?
So it might be sufficient to simply test that action.
describe CustomersController do
it "creates customer if :api_key is present` do
post :create, api_key: "present key", customer_attributes
expect(Customer.count).to eq 1
end
it "does not create customer if :api_key is absent` do
json_error = {
key1: 'value1',
key2: 'value2'
}.to_json
post :create, customer_attributes
expect(response.body).to eq json_error
end
end
You can test the method directly, if you set up the params.
describe CustomersController do
it "creates customer if :api_key is present' do
controller.params[:api_key] = 'present key'
controller.params.merge!(customer_attributes)
controller.customer_create
expect(Customer.count).to eq 1
end
end
Both examples assume customer attributes hash is stored in a variable customer_attributes

Resources