I'm trying to DRY my code by putting a hash into a let and just calling the let but it doesn't seem to work. I get these errors:
syntax error, unexpected =>, expecting '}' (SyntaxError) "email" => user.email,
syntax error, unexpected =>, expecting :: or '[' or '.' ...l" => user.email, "password" => user.password }
Here's my test:
describe '#create' do
let(:user) { create(:user) }
let(:user_params) { "email" => user.email, "password" => user.password }
before(:each) { User.stub(:find_by_email).and_return(user) }
it "should send find message to user model" do
User.should_receive(:find_by_email)
post :create, locale: "es", user: { user_params }
end
it "should set user to #user" do
post :create, locale: "es", user: { "email" => user.email, "password" => user.password }
assigns(:user).should eq(user)
end
end
And here's my code:
def create
#user = User.find_by_email(params[:email])
if #user && #user.authenticate(params[:password])
session[:user_id] = #user.id
redirect_to root_url
else
render 'login'
end
end
You're missing one pair of braces.
let(:user_params) { "email" => user.email, "password" => user.password }
should be
let(:user_params) { {"email" => user.email, "password" => user.password} }
I would prefer a bit longer explanation.
When you write something like this:
let(:user_params) { "email" => user.email, "password" => user.password }
You call method let with one parameter (:user_params) and a block (in this case { "email" => user.email, "password" => user.password }). I will state it again:
let(:something) { some_method_call }
and
let(:something) do
some_method_call
end
are equal. By using let, RSpec sets variable to the result of the block. So, inside of the block, you need to return something. In this case - hash:
let(:user_params) do
{ "email" => user.email, "password" => user.password }
end
That's it! You can find out more about let on APIdock
Related
I wrote this code for testing controller update function.
Wrote a method for eliminating duplicate code.
Is this an explicit way to do it?
users_controller_spec.rb
context 'Update failed' do
def render_edit
user.reload
expect(response.status).to eq(200)
end
it 'Name is nil' do
put :update, params: { id: user.id, user: { name: '' } }
render_edit
end
it 'Email is exist' do
create(:user, email: 'user#gmail.com')
put :update, params: { id: user.id, user: { email: 'user#gmail.com' } }
render_edit
end
it 'Email is nil' do
put :update, params: { id: user.id, user: { email: '' } }
render_edit
end
it 'Password must be at least 8 characters' do
put :update, params: { id: user.id, user: { password: '1234567', password_confirmation: '1234567' } }
render_edit
end
it 'Passwords do not match' do
put :update, params: { id: user.id, user: { password: '1234567890', password_confirmation: '123456789' } }
render_edit
end
end
I was thinking to use after(:each). But it looks a little wired in logic.
Or use loop to replace params.
Any suggestion?
You can use shared examples as suggested in the comments, but there's an easier way.
context 'Update failed' do
before do
put :update, params: params
user.reload # I'm not sure why you need this
end
subject { response }
context 'Name is nil' do
let(:params} { {id: user.id, user: { name: '' }} }
it { is_expected.to be_success }
end
context 'Email exists' do
let(:params) { { id: user.id, user: { email: 'user#gmail.com' } }
let(:user) { create(:user, email: 'user#gmail.com') }
it { is_expected.to be_success }
end
# and so on
end
The main rune I use is - make it obvious what change in each context. So instead of redefining put ..., extract it as a let and define it per context.
be_success is part of rspec magic, wherever you use be_something matcher it'll try to use something? method and check if it's true, i.e.
expect(foo).to be_empty? == expect(foo.empty?).to eq(true)
If you don't want it make it like this
subject { response.status }
# and later
is_expected.to eq 200
is_expected.to is just a shorthand for expect(subject).to
i'm writing the code to get my Rspec tests to pass on my api. I'm using the apipie gem to generate documentation and it seems that my tests are failing because thy are expecting a number and it's funny because this is exactly what I want to test.
The page fails when the :bpm parameter is not a number. is there any way of going around this ?
context "when is not created" do
before(:each) do
user = FactoryGirl.create :user
#invalid_lesson_attributes = { title: "California Dreamin",
bpm: "Hello"
}
request.headers['Authorization'] = user.auth_token
post :create, { user_id: user.id, lesson: #invalid_lesson_attributes }
end
it "renders an errors json" do
lesson_response = json_response
expect(lesson_response).to have_key(:errors)
end
it "renders the json errors on why the user could not be created" do
lesson_response = json_response
expect(lesson_response[:errors][:bpm]).to include "is not a number"
end
it { should respond_with 422 }
end
end
Update spec:
context "when is not updated" do
before(:each) do
patch :update, { user_id: #user.id, id: #lesson.id,
lesson: { bpm: "ten" }, format: :json }
end
it "renders an errors json" do
lesson_response = json_response
expect(lesson_response).to have_key(:errors)
end
it "renders the json errors on why the user could not be updated" do
lesson_response = json_response
expect(lesson_response[:errors][:bpm]).to include "is not a number"
end
it { should respond_with 422 }
end
in my users_controller:
api :POST, '/teachers/:user_id/lessons/', "Create lesson"
param :lesson, Hash, desc: 'Lesson information', :required => true do
param :title, String, desc: 'Title of the lesson', :required => true
param :bpm, :number, desc: 'tempo of the lesson (beats per second)', :required => true
end
error :code => 422, :desc => "Unprocessable Entity"
my error when I run my rspec tests :
Apipie::ParamInvalid: Invalid parameter 'bpm' value "Hello": Must be a number.
Adds format json to post request
post :create, { user_id: user.id, lesson: #invalid_lesson_attributes, format: :json }
That worked for me.
I'm writing a Rails API and am stuck trying to write controllers that will test the authentication. For instance, I have in my PostController before_action :authenticate which is
def authenticate
authenticate_or_request_with_http_token do |token, options|
User.find_by(:auth_token => token)
end
end
And this is my PostsController test:
require 'rails_helper'
RSpec.describe Api::PostsController, type: :controller do
let(:valid_attributes) {
{
"title" => "Post title",
"content" => "Post content",
"status" => "published"
}
}
let(:invalid_attributes) {
{
"title" => "",
"content" => "",
"status" => ""
}
}
let(:valid_session) { {} }
before do
params = { session: { email: 'wagner.matos#mac.com', password: 'foobar' } }
SessionsController.create params
#post = Post.new({
title: "Some title",
content: 'Some content',
status: "published"
})
end
it "creates a new post" do
post :create, post: #post
expect(Post.count).to eq(1)
end
end
Yet the above is failing with the following error:
1) Api::PostsController creates a new post
Failure/Error: SessionsController.create params
NoMethodError:
undefined method `create' for SessionsController:Class
Any suggestions?
You can not invoke create action of SessionsController with class. Better you create user object and assign to request.env the same token. like below sample code -
require 'rails_helper'
RSpec.describe Api::PostsController, type: :controller do
before do
user = User.create( :auth_token => 'token' )
request.env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Token.encode_credentials("token")
end
end
I'm told this is the correct way to write a controller it block:
describe UsersController do
let(:user){ mock_model(User, id: 2, name: "Jimbo", email: 'jimbo#email.com', password: 'passwordhuzzah', password_confirmation: 'passwordhuzzah') }
describe 'PATCH #update' do
it "should fail in this case" do
User.should_receive(:find).with(user.id.to_s).and_return user
user.should_receive(:update_attributes).with({ "email" => user.email, "name" => user.name, "password" => user.password, "password_confirmation" => user.password_confirmation }).and_return true
patch :update, id: user.id, user: { email: user.email, name: user.name, password: user.password, password_confirmation: user.password_confirmation }
flash[:error].should == "could not update user"
response.status.should == 200
end
end
end
But I am surprised, because it doesn't seem DRY to me. Let's say I want to create two 'contexts' here. One where the user.update_attributes call returns false and returns true. Am I meant to simply copy paste two it blocks and tweak that one tiny argument?
describe UsersController do
let(:user){ mock_model(User, id: 2, name: "Jimbo", email: 'jimbo#email.com', password: 'passwordhuzzah', password_confirmation: 'passwordhuzzah') }
it "should pass in this case" do
User.should_receive(:find).with(user.id.to_s).and_return user
user.should_receive(:update_attributes).with({ "email" => user.email, "name" => user.name, "password" => user.password, "password_confirmation" => user.password_confirmation }).and_return true
patch :update, id: user.id, user: { email: user.email, name: user.name, password: user.password, password_confirmation: user.password_confirmation }
flash[:error].should == "updated user"
response.status.should == 302
end
it "should fail in this case" do
User.should_receive(:find).with(user.id.to_s).and_return user
user.should_receive(:update_attributes).with({ "email" => user.email, "name" => user.name, "password" => user.password, "password_confirmation" => user.password_confirmation }).and_return false
patch :update, id: user.id, user: { email: user.email, name: user.name, password: user.password, password_confirmation: user.password_confirmation }
flash[:error].should == "could not update user"
response.status.should == 200
end
end
end
I was hoping rspec would allow me to write this sort of structure:
describe 'PATCH #update' do
before {}
context 'when attributes can be updated' do
before {}
it "should set the flash" do end
it "should set the status" do end
end
context 'when attributes can\'t be updated' do
before {}
it "should set the status" do end
it "should set the flash" do end
end
end
Note the multiple it blocks for the same contexts, because isn't one expectation per it block a good practise because it allows you to see exactly what's not working? How are you meant to do this with mocks?
Model Validation:
validates :username, uniqueness: true, format: { with: /\A[a-zA-Z0-9_.-#]+\Z/i, message: "must contain only letters, numbers or _*-#" }, on: :update, :if => :username_changed?
Rspec:
require 'spec_helper'
describe User, "references" do
it { should have_and_belong_to_many(:roles) }
it { should belong_to(:account_type) }
it { should belong_to(:primary_sport).class_name("Sport") }
it { should belong_to(:school) }
it { should belong_to(:city) }
it { should validate_presence_of(:email) }
it { should validate_uniqueness_of(:email) }
it { should allow_value("test#test.com").for(:email) }
it { should_not allow_value("test.com").for(:email) }
describe "validation of username", focus: true do
before(:each) do
#user = User.new(email: Faker::Internet.email, password: "password", password_confirmation: "password", username: "test123", agreed_to_age_requirements: true)
end
it "should be valid" do
#user.save
#user.should be_valid
end
it "should not be valid with incorrect characters in username" do
#user.username = "test###!!!"
#user.should_not be_valid
end
end
end
FactoryGirl:
FactoryGirl.define do
factory :user do
email Faker::Internet.email
password "password"
password_confirmation "password"
agreed_to_age_requirements true
username Faker::Internet.user_name
end
end
I am basically just trying to test against the custom validation for the uniqueness of and with the specified format of the username