RSpec test model and mailer - ruby-on-rails

I have method in model
(User Model)
def create_reset_code
self.attributes = {:reset_code => Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )}
save(:validate=>false)
UserMailer.reset_password_email(self).deliver
end
How can I test it in RSpec? I want to test the code generation, and send e-mail
PS : using Google, but found no examples of
UPD
I write two tests:
it "should create reset code" do
#user.create_reset_code
#user.reset_code.should_not be_nil
end
it "should send reset code by email" do
#user.create_reset_code
#email_confirmation = ActionMailer::Base.deliveries.first
#email_confirmation.to.should == [#user.email]
#email_confirmation.subject.should == I18n.t('emailer.pass_reset.subject')
#email_confirmation.body.should match(/#{#user.reset_code}/)
end
But this --- #email_confirmation.body.should match(/#{#user.reset_code}/) ---- don't work
In a letter I give url with reset_code as follow reset_password_url(#user.reset_code)
FIXED
it "should send reset code by email" do
#user.create_reset_code
#email_confirmation = ActionMailer::Base.deliveries.last
#email_confirmation.to.should == [#user.email]
#email_confirmation.subject.should == I18n.t('emailer.pass_reset.subject')
#email_confirmation.html_part.body.should match /#{#user.reset_code}/
end
It's work!
Thank you all, question is closed

it "should create reset code" do
#user.create_reset_code
#user.reset_code.should_not be_nil
end
it "should send reset code by email" do
#user.create_reset_code
#email_confirmation = ActionMailer::Base.deliveries.last
#email_confirmation.to.should == [#user.email]
#email_confirmation.subject.should == I18n.t('emailer.pass_reset.subject')
#email_confirmation.html_part.body.should match /#{#user.reset_code}/
end

you can use
mail = ActionMailer::Base.deliveries.last
to get the last email sent after calling that method on the spec, then you can do specs against mail.to or mail.body.raw_source

Something like this should help you.. I have used Rspec matchers
it "should test your functionality" do
user = Factory(:ur_table, :name => 'xyz', :email => 'xyz#gmail.com')
obj = Factory(:ur_model_table)
key = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join
data = obj.create_reset_code
data.reset_code.should be(key)
let(:mail) {UserMailer.reset_password_email(user) }
mail.to.should be(user.email)
end

Related

Testing for uniqueness of before_create field in Rails and Rspec

I have a private method that generates a unique open_id for each user. The open_id is also indexed on the database level. How do I write a model test for uniqueness in RSpec?
before_create: generate_open_id!
def generate_open_id!
begin
self.open_id = SecureRandom.base64(64)
end while self.class.exists?(open_id: self.open_id)
end
UPDATE: solution based on accepted answer below
def generate_open_id!
if !self.open_id
begin
self.open_id = SecureRandom.base64(64)
end while self.class.exists?(open_id: self.open_id)
end
end
#users = FactoryGirl.create_list(:user, 10)
#user_last = #users.last
subject { #user_last }
it "has a random open_id" do
base_64_regex = %r{^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$}
expect(#user_last.open_id).to match base_64_regex
end
it "has a unique open_id" do
expect {FactoryGirl.create(:user, open_id: #user_last.open_id)}.to raise_error(ActiveRecord::RecordNotUnique)
end
Refactoring your original code will make testing what you're trying to do much easier. Change your generate_open_id! method to this
def generate_open_id!
(open_id = SecureRandom.base64(64)) unless open_id
end
And now you can test with the following
# spec/models/some_model_spec.rb
describe SomeModel do
subject(:some_model){ FactoryGirl.create(:some_model) }
describe 'open_id attribute' do
it 'is a random base64 string' do
base_64_regex = %r{^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$}
expect(some_model.open_id).to match base_64_regex
end
it 'is unique' do
expect {FactoryGirl.create(:some_model, open_id: some_model.open_id)}.to raise_error(ActiveRecord::RecordInvalid)
end
end
end
You can use SecureRandom.uuid that will generate to you unique strings.
More info here.
Also, you can add validates_uniqueness_of :your_field that will do it for you.

How to delete an entire array in Ruby and test with RSpec

I'm fairly new to Ruby and am currently taking a full stack course. For one of my projects we are building an addressbook. I have set up how to add an entry to the addressbook, however, I can't seem to figure out how to delete an entry (I make an attempt with the remove_entry method in the AddressBook class below but am not having any luck). We are also supposed to test first with RSpec, have the test fail and then write some code to get it to pass. If I didn't include all the info needed for this question let me know (rookie here). Anyway, here is what I have so far:
RSpec
context ".remove_entry" do
it "removes only one entry from the address book" do
book = AddressBook.new
entry = book.add_entry('Ada Lovelace', '010.012.1815', 'augusta.king#lovelace.com')
book.remove_entry(entry)
expect(entry).to eq nil
end
end
AddressBook class
require_relative "entry.rb"
class AddressBook
attr_accessor :entries
def initialize
#entries = []
end
def add_entry(name, phone, email)
index = 0
#entries.each do |entry|
if name < entry.name
break
end
index += 1
end
#entries.insert(index, Entry.new(name, phone, email))
end
def remove_entry(entry)
#entries.delete(entry)
end
end
Entry class
class Entry
attr_accessor :name, :phone_number, :email
def initialize(name, phone_number, email)
#name = name
#phone_number = phone_number
#email = email
end
def to_s
"Name: #{#name}\nPhone Number: #{#phone_number}\nEmail: #{#email}"
end
end
When testing my code with RSpec I receive the following error message:
.....F
Failures:
1) AddressBook.remove_entry removes only one entry from the address book
Failure/Error: expect(entry).to eq nil
expected: nil
got: [#<Entry:0x00000101bc82f0 #name="Ada Lovelace", #phone_number="010.012.1815", #email="augusta.king#lovelace.com">]
(compared using ==)
# ./spec/address_book_spec.rb:49:in `block (3 levels) in <top (required)>'
Finished in 0.02075 seconds (files took 0.14221 seconds to load)
6 examples, 1 failure
Failed examples:
rspec ./spec/address_book_spec.rb:44 # AddressBook.remove_entry removes only one entry from the address book
Just test that the book.entries association is empty:
expect(book.entries).to be_empty
As book is a local variable in your test, you will not get a false negative result if you keep your test atomic. Some best practices on rspec.
Edit:
You can also check the entry was not in the set:
expect(book.entries.index(entry)).to be_nil
or test the change of the array length with:
expect { book.remove_entry(entry) }.to change{book.entries.count}.by(-1)
If you wonder for the be_xxx syntax sugar, if the object respond to xxx?, then you can use be_xxx in your tests (predicate matchers)
I think your expect has an issue. The entry variable is not set to nil, but the entry inside book would be nil.
I think something like this would work better:
expect(book.entries.find { |e| e.name == "Ada Lovelace" }).to eq nil
Better still, your AddressBook could have its own find method, which would make the expect param much nicer, like book.find(:name => "Ada Lovelace").
Finally, I would also put an expect call before the remove_entry call, to make sure its result equals entry.

Rails rspec after_save reference self

I want to add a profanity check on my website.
I'm taking a TD approach and I'm trying the following:
check if profanity exists in specific profile fields
create a flag
create a flag if one does not exist
create a flag if one exists, but has been dismissed
Here is my spec so far:
describe Painter do
before do
#painter = FactoryGirl.create(:painter_flag)
end
context "blacklist flag" do
it "check if profanity exists" do
#painter.experience = "test"
#painter.save
expect {#painter.blacklist_flags?}.to be_true
end
it "create flag if profanity exists" do
#painter.experience = "test"
#painter.save
BlacklistFlag.count.should be > 0
end
end
end
Painter related code:
after_save :create_flag, if: :blacklist_flags?
def blacklist_flags?
list = ""
list << skills
#list << experience
#list << first_name
#list << last_name
#list.downcase.scan(/(badword|badword)/).size > 0
end
def create_flag
end
If I comment out the following code above the two test pass:
list << skills
When I leave the code in I receive the following error:
2) Painter blacklist flag create flag if profanity exists
Failure/Error: #painter = FactoryGirl.create(:painter_flag)
TypeError:
can't convert nil into String
It seems there's a problem with referencing self because skills, experience, etc are part of the model. I'm not sure how to fix this. Please advise.
Update:
FactoryGirl.define do
factory :painter do
first_name "Brian"
last_name "Rosedale"
state "OH"
zip_code "43081"
sequence(:email) {|n| "nobody#{n}#painterprofessions.com" }
phone "12345566"
pdca_member false
password "123456"
factory :painter_flag do
skills = "badword"
end
end
end
Just use this line in your factory for :painter_flag, without the = sign.
skills "badword"
I think what's causing the error is because the callback is been executed on the line #painter = FactoryGirl.create(:painter_flag).
You might want to use FactoryGirl.build method if you want to test the callback.

How do I stub out the flickraw library in my app's unit tests?

My Rails 2 app displays a slideshow of photos from Flickr via the flickraw library. My code works, but I'm stuck on how to properly write RSpec unit tests.
I have a Slide class that encapsulates everything that my app needs from flickraw. It acts somewhat like a model object but doesn't use ActiveRecord. It doesn't do much work; it delegates most of the heavy lifting to flickraw.
I haven't completed the tests because, as it is now, they require me to hard-code in some photo IDs from Flickr, and the tests would break if I rearranged my photoset or added new photos.
So my so-called unit tests are more like integration tests. I understand how to write a mock or stub using RSpec, but not sure how to do it to the flickraw library. How do I stub out flickraw and turn this into a unit test?
slide.rb:
require 'flickraw'
FlickRaw.api_key = "xxx"
FlickRaw.shared_secret = "yyy"
flickr.auth.checkToken :auth_token => "zzz"
PHOTOSET_ID = 123123123
class Slide
attr_accessor :id, :previous_id, :next_id, :url_square, :url_thumbnail, :url_small, :url_medium500,
:url_medium640, :url_large, :url_original
def self.last
photoset = flickr.photosets.getPhotos(:photoset_id => PHOTOSET_ID)
Slide.new(photoset.photo.last.id)
end
def self.first
photoset = flickr.photosets.getPhotos(:photoset_id => PHOTOSET_ID)
Slide.new(photoset.photo.first.id)
end
def self.find(id)
Slide.new(id)
end
def initialize(id)
self.id = id
photo = flickr.photos.getInfo(:photo_id => id)
context = flickr.photosets.getContext(:photoset_id => PHOTOSET_ID, :photo_id => id)
sizes = flickr.photos.getSizes(:photo_id => id)
self.previous_id = (context.prevphoto.id == 0) ? nil : context.prevphoto.id
self.next_id = (context.nextphoto.id == 0) ? nil : context.nextphoto.id
sizes.each do |size|
if size.label == "Square"
self.url_square = size.source
elsif size.label == "Thumbnail"
self.url_thumbnail = size.source
elsif size.label == "Small"
self.url_small = size.source
elsif size.label == "Medium"
self.url_medium500 = size.source
elsif size.label == "Medium 640"
self.url_medium640 = size.source
elsif size.label == "Large"
self.url_large = size.source
elsif size.label == "Original"
self.url_original = size.source
end
end
end
end
slide_spec.rb:
require 'spec_helper'
describe Slide do
before(:each) do
first_photo_id = "444555666"
#slide = Slide.new(first_photo_id)
end
describe "urls" do
it "should generate the thumbnail url" do
#slide.url_thumbnail.should match(/_t.jpg$/)
end
it "should generate the small url" do
#slide.url_small.should match(/_m.jpg$/)
end
it "should generate the medium500 url" do
#slide.url_medium500.should match(/.jpg$/)
end
it "should generate the medium640 url" do
#slide.url_medium640.should match(/_z.jpg$/)
end
it "should generate the large url" do
#slide.url_large.should match(/_b.jpg$/)
end
it "should generate the original url" do
#slide.url_original.should match(/_o.jpg$/)
end
end
describe "finding" do
it "should find the correct last photo" do
# ???
end
it "should find the correct first photo" do
# ???
end
end
describe "context" do
it "should return the correct previous photo" do
# ???
end
it "should return the correct next photo" do
# ???
end
end
end
As I understand it, you should be able to do slide.stub!(:flickr).and_return to mock out anything that isn't inside the constructor. I query the fact that the constructor is loading so much from the flickr api though.
Can you change the implementation of slide so that instead of a load of attr_accessors, you have actual methods that get stuff from the flickr api? You should be able to do this without changing the external api of the class, and you can enable speed via caching the results of api calls in instance variables.
If you still want to have all that work done in the constructor, I'd recommend having a default argument that represents the flickr service. Your constructor then becomes the following:
def initialize(id, flickr=flickr)
… complicated setup code here…
end
This way, when you're testing, just pass in a mock flickr object like so:
flickr = mock(:flickr)
#slide = Slide.new(image_id, flickr)
You can then write the usual rspec assertions against the new flickr object (again, without changing the external api).

Specing a manual call to valid?

Hey all, I am completely lost on this one.
I found a code snippet online to help validate fields via ajax as the user types into them. So I'm trying to write a spec against part of it and I just can't get it to pass.
Here's the code
def validate
field = params[:field]
user = User.new(field => params[:value])
output = ""
user.valid?
if user.errors[field] != nil
if user.errors[field].class == String
output = "#{field.titleize} #{user.errors[field]}"
else
output = "#{field.titleize} #{user.errors[field].to_sentence}"
end
end
render :text => output
end
and here is my test so far
describe "POST validate" do
it "retrieves the user based on the past in username" do
mock_errors ||= mock("errors")
mock_errors.stub!(:[]).and_return(nil)
User.should_receive(:new).with({'username'=>"UserName"}).and_return(mock_user)
mock_user.should_receive(:valid?).and_return(true)
mock_errors.should_receive(:[]).with("username").and_return(nil)
put :validate, :field=>'username', :value=>'UserName'
response.should == ""
end
end
I get this error -
1) Spec::Mocks::MockExpectationError
in 'UsersController POST validate
retrieves the user based on the past
in username' Mock 'errors' received
unexpected message :[] with
("username")
I can't seem to figure out how in the world to mock the call to user.errors[field]. Ideally this spec tests the happy path, no errors. I'll then write another for a validation failure.
I'm not seeing mock_user. Here's a shot at it:
describe "POST validate" do
it "retrieves the user based on the past in username" do
mock_errors = mock("errors")
mock_user = mock("user")
mock_user.stub!(:errors).and_return([mock_errors])
mock_errors.stub!(:[]).and_return(nil)
User.should_receive(:new).with({'username'=>"UserName"}).and_return(mock_user)
mock_user.should_receive(:valid?).and_return(true)
mock_errors.should_receive(:[]).with("username").and_return(ActiveRecord::Errors.new({}))
put :validate, :field=>'username', :value=>'UserName'
response.should == ""
end
end
The key is that you need your User mock to respond to the errors method by returning either an empty hash or a hash of fieldname/errors. An alternative to this is to use one of the fixture replacement tools. I'm using machinist right now, which might reduce this whole thing to:
describe "POST validate" do
it "retrieves the user based on the past in username" do
#user = User.make{'username'=>"UserName"}
#user.should_receive(:valid?).and_return(true)
#user.errors.should_receive(:[]).with("username").and_return(ActiveRecord::Errors.new({}))
put :validate, :field=>'username', :value=>'UserName'
response.should == ""
end
end

Resources