My question is, why do I receive the following rspec error message? (code below) I've stubbed the :update_payment method on the StripeSubscription model. I've been at this for a couple of hours and am perplexed.
Failure/Error: #stripe_msub.should_receive(:update_payment).and_return(#stripe_msub)
(#<StripeSubscription:0xb879154>).update_payment(any args)
expected: 1 time
received: 0 times
###Rspec test###
describe "PUT 'update'" do
context "signed-in teacher" do
before(:each) do
#teacher = Factory(:teacher)
#teacher_upload = Factory(:teacher_upload,:teacher_id=>#teacher.id)
#stripe_mplan = Factory(:stripe_plan)
#new_stripe_card_token = 528
#stripe_msub = Factory(:stripe_subscription,:teacher_id=>#teacher.id,:stripe_plan_id=>#stripe_mplan.id, :email=>#teacher.email,:account_status=>Acemt::Application::STRIPE_SUBSCRIPTION_ACCOUNT_STATUS[:active])
#stripe_msub.stub!(:update_payment).and_return(#stripe_msub)
StripeSubscription.stub!(:update_payment).and_return(#stripe_msub)
StripeSubscription.stub!(:update_attributes).and_return(true)
#stripe_customer = mock('Stripe::Customer')
Stripe::Customer.stub!(:retrieve).with(#stripe_msub.stripe_customer_token).and_return(#stripe_customer)
#stripe_customer.stub(:card=).and_return(true)
#stripe_customer.stub(:save).and_return(true)
test_sign_in(#teacher)
end
it "should update credit card information" do
#stripe_msub.should_receive(:update_payment)
Stripe::Customer.should_receive(:retrieve).with(#stripe_msub.stripe_customer_token).and_return(#stripe_customer)
#stripe_customer.should_receive(:card=)
#stripe_customer.should_receive(:save).and_return(#stripe_customer)
put :update, :teacher_id=>#teacher.id, :stripe_subscription=>{:stripe_plan_id=>#stripe_msub.stripe_plan_id, :teacher_id=>#stripe_msub.teacher_id, :email=>#stripe_msub.email, :stripe_customer_token=>#stripe_msub.stripe_customer_token,:stripe_card_token=>#new_stripe_card_token,:account_status=>#stripe_msub.account_status}
#teacher.stripe_subscription.should == #stripe_msub
response.should redirect_to teacher_path(#teacher)
end
end #signed in teacher
end #PUT update
###controller###
class StripeSubscriptionsController < ApplicationController
before_filter :signed_in_teacher
before_filter :correct_teacher
:
def update
##stripe_subscription = StripeSubscription.find_by_id(params[:id])
#stripe_subscription = #teacher.stripe_subscription
if #stripe_subscription.update_payment(params[:stripe_subscription])
#handle successful update
flash[:success] = "Credit card updated"
sign_in #teacher
redirect_to #teacher
else
render 'edit'
end
end
:
end
###model###
class StripeSubscription < ActiveRecord::Base
#attr_accessible :email, :plan_id, :stripe_customer_token, :teacher_id, :account_status
validates_presence_of :stripe_plan_id
validates_presence_of :email
validates_presence_of :teacher_id
belongs_to :stripe_plan, :class_name=>"StripePlan"
belongs_to :teacher
attr_accessor :stripe_card_token
def save_with_payment
if valid?
customer = Stripe::Customer.create(description: email, plan: stripe_plan_id, card: stripe_card_token)
self.stripe_customer_token = customer.id
save!
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card."
end
def update_payment(stripe_params)
if valid?
customer = Stripe::Customer.retrieve(self.stripe_customer_token)
customer.card = stripe_params[:stripe_card_token]
status = customer.save #update card info on Stripe
update_attributes(stripe_params) #save StripeSubscription object
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while updating your credit card: #{e.message}"
errors.add :base, "There was a problem with your credit card."
end
end
You are setting the expectation on an object in your spec (#stripe_msub). In the controller however, you probably load a teacher from the database and get its subscription (#stripe_subscription).
Due to the roundtrip through the database, this object is not the same one as the one you set your expectation on, therefore the one you set your expection on (#stripe_msub in the spec) never receives the method call and therefore rspec complains.
To fix this, you would have to stub away all database calls and make sure, that your object from the spec appears in the controller. I don't know where exactly #teacher is set in the controller (I guess in one of the filters) so I can't give you the exact solution, but it will be something like this:
# I assume this is the implentation of your filter in the controller
def signed_in_teacher
#teacher = Teacher.find_by_id(params[:teacher_id])
end
# Then you would have to add the following mocks/stubs to your spec
Teacher.should_receive(:find_by_id).once.with(#teacher.id).and_return(#teacher)
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I made some variables that is related to models and I want to save the new variable relating to the control structure, but I can't. It said "NoMethodError - undefined method `save' for 1:Fixnum:".
What I want to make is that the function if This program get 4 people, It will show the member name for that members. 5th member will be in the next group.
Anyone can solve this?
or if you need more information, please let me know.
Thanks
This is Waitinglists_controller
class WaitinglistsController < ApplicationController
before_action :authenticate
def new
#waitinglist = current_user.created_waitinglists.build
end
def create
#waitinglist = current_user.created_waitinglists.build(waitinglist_params)
if #waitinglist.save
redirect_to waitinglist_waiting_path(#waitinglist, #owner)
else
render :new
end
end
def waiting
#group_number = Waitinglist.select(:count_number).last
#already_group_people = Waitinglist.where(count_number: #group_number).count
#current_person_group_number = current_user.created_waitinglists.select(:count_number)
#current_group_people = Waitinglist.where(count_number: #current_person_group_number).count
case #already_group_people
when 0
#current_person_group_number = 1
#current_person_group_number.save
when 1..2
#current_person_group_number = #group_number
#current_person_group_number.save
when 3
#current_person_group_number = #group_number
#current_person_group_number.save
redirect_to show_waitinglist_path
when 4
group_number += 1
#current_person_group_number = #group_number
#current_person_group_number.save
end
end
def show
#current_person_group_number = current_user.created_waitinglist.select(:count_number)
#matched_people = Waitinglist.find(count_number: #current_person_group_number)
#matched_people == 0 if #matched_people = nil
end
private
def created_by?(user)
return false unless user
owner_id == user.id
end
def waitinglist_params
params.require(:waitinglist).permit(:look_like, :id)
end
end
This is Sessions controller for User loggin
class SessionsController < ApplicationController
def create
user = User.find_or_create_from_auth_hash(request.env['omniauth.auth'])
session[:user_id] = user.id
redirect_to root_path
end
def destroy
reset_session
redirect_to root_path
end
end
Application contrtoller is this
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :current_user, :logged_in?
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def logged_in?
!!session[:user_id]
end
def authenticate
return if logged_in?
redirect_to root_path
end
end
Under codes are for models
class Waitinglist < ActiveRecord::Base
belongs_to :waiting_person, class_name: 'User'
after_initialize :init
def init
self.count_number ||= 1 #will set the default value only if it's nil
end
end
class User < ActiveRecord::Base
has_many :created_waitinglists, class_name: 'Waitinglist', foreign_key: :owner_id
def self.find_or_create_from_auth_hash(auth_hash)
provider = auth_hash[:provider]
uid = auth_hash[:uid]
name = auth_hash[:info][:name]
image_url = auth_hash[:info][:image]
User.find_or_create_by(provider: provider, uid: uid) do |user|
user.nickname = name
user.image_url = image_url
end
end
end
It's quite clear to me. You call save on #current_person_group_number, which is instance of Fixnum, so it doesn't have save method defined.
Unfortunately the code you wrote makes very little sense and it is pretty hard to understand what you're trying to do here.
Firstly, you overuse instance variables. If you're not gona use them in another methods (it is hard to say as you haven't post rest of your class)
Secondly, you overuse select method.
#group_number = Waitinglist.select(:count_number).last
All it does is changing the SELECT statement when querying the database for models, but it still returns the model, not a number or field value. So #group_number is not a number - it is a WaitingList instance. If you want a number do:
group_number = WaitingList.last.count_number
(posting now as question may be closed in a second. Will update later if that won't happen)
You can only save the ActiveRecord objects, which means you need to have it somewhere. Apparantly you want to update #current_person_group_number, however you can't reassign this variable to do the trick. You have to get the whole model, change its attribute and then save the model. It would look sth like:
current_waiting_list = current_user.created_waitinglists.last # This seems to be a collection, you need to tell here which waiting list you want to get from this collection
current_waiting_list.count_number += 1
current_waitin_list.save
My last point is - please look into act_as_list gem. Since you're creating waiting list it is a must have gem for you.
I'm using Active Merchant with Stripe as the payment gateway. Everything works fine except that i don't know how to go about getting the gateway response error messages from Stripe (when a card is declined, invalid etc) to display on the checkout page to the user. I can get a StandardError to be raised that redirects to an error page with the response message but that's it.
ORDER MODEL
class Order < ActiveRecord::Base
has_many :order_products
has_many :products, through: :order_products
attr_accessor :card_number, :security_code, :card_expires_on
validate :validate_card, :on => :create
def validate_card
unless credit_card.valid?
credit_card.errors.full_messages.each do |message|
errors[:base] << message
end
end
end
def purchase(basket)
response = GATEWAY.purchase(Product.total_basket_price(basket)*100, credit_card, purchase_options)
unless response.success?
raise StandardError, response.message
end
end
def credit_card
#credit_card ||= ActiveMerchant::Billing::CreditCard.new(
:number => card_number,
:first_name => first_name,
:last_name => last_name,
:verification_value => security_code,
:month => card_expires_on.month,
:year => card_expires_on.year
)
end
def purchase_options
{
:billing_address => {
:address1 => address_1,
:address2 => address_2,
:city => city,
:country => country_code,
:zip => postal_code
}
}
end
end
ORDERS CONTROLLER
class OrdersController < ApplicationController
def create
#order = Order.new(order_params)
# #product = basket.find(params[:product_id])
basket.each do |item_id|
#order.order_products.build(product: Product.find(item_id))
end
if #order.save
if #order.purchase(basket)
render "show"
else
render "failure"
end
else
render "new"
end
end
Can anyone lend a hand, please??
Many Thanks
Easy peasy!
This is a simple matter of control flow. In Ruby, as in most languages, exceptions interrupt the normal program flow. As your code is written now, #purchase is raising an exception when it fails.
That's fine and a perfectly valid design decision. But the code interacting with #purchase is this:
if #order.purchase(basket)
render "show"
else
render "failure"
end
That code has no exception handling, so any exception will be caught by Rails, program flow will halt, and you'll get either a detailed error page (in development mode) or a generic 500 error page (in production mode).
Since you profess to be new to Ruby and Rails, a little code substitution might make this clearer:
# If #purchase is successful, it evaluates to true.
if true
render "show" # 'show' view is rendered as expected. Flow stops.
else
render "failure"
end
# If #purchase fails, it raises an exception.
if raise StandardError, response.message
# ^^^ Exception is raised, flow stops here.
render "show" # This code is never reached.
else # This code is never reached.
render "failure" # This code is never reached.
end
As I implied in the beginning, though, it's an easy fix once you know what the issue is. You can simply handle the exception with rescue. Where you currently have an if/else block, you can swap in an if block and a rescue block:
if #order.purchase(basket)
render 'show'
end
rescue => e
render 'failure'
There's room for improvement here depending on your needs—since you're raising and rescuing StandardError, for example, your can't easily distinguish between a network failure and a declined card—but it'll get you moving again.
After a lot of fiddling and help, the working solution was to search for an error key within the response params hash and if an error was present add the message to the object errors. Not particularly elegant but it now does what i want.
ORDER MODEL
def purchase(basket)
response = GATEWAY.purchase(Product.total_basket_price(basket)*100, credit_card, purchase_options)
if response.params.key?('error')
self.errors.add :base, response.message
false
else
true
end
end
ORDERS CONTROLLER
Also switched the order of the if statements in the controller so that def purchase(basket) runs first before the order is saved, allowing the error message(s) from the response to be caught and displayed.
if #order.purchase(basket)
if #order.save
render "show"
else
render "new"
end
else
render "new"
end
VIEW
<%= if #order.errors.any?
#order.errors[:base].to_sentence
end%>
Sorcery authentication gem: https://github.com/NoamB/sorcery
Sorcery's creator provides an example Rails app with Sorcery test helpers included in its Test::Unit functional tests: https://github.com/NoamB/sorcery-example-app/blob/master/test/functional/users_controller_test.rb
# Test::Unit functional test example
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
setup do
#user = users(:noam)
end
test "should show user" do
login_user
get :show, :id => #user.to_param
assert_response :success
end
But I can't figure out how to get login_user to work in my RSpec controller specs.
/gems/sorcery-0.7.5/lib/sorcery/test_helpers/rails.rb:7:in `login_user':
undefined method `auto_login' for nil:NilClass (NoMethodError)
Here's the relevant code in the Sorcery gem regarding the above error:
https://github.com/NoamB/sorcery/blob/master/lib/sorcery/test_helpers/rails.rb
module Sorcery
module TestHelpers
module Rails
# logins a user and calls all callbacks
def login_user(user = nil)
user ||= #user
#controller.send(:auto_login,user)
#controller.send(:after_login!,user,[user.send(user.sorcery_config.username_attribute_names.first),'secret'])
end
def logout_user
#controller.send(:logout)
end
end
end
end
UPDATE:
As per Sorcery's documentation "Testing in Rails 3", I have indeed added include Sorcery::TestHelpers::Rails to my spec_helper.rb.
The Sorcery test helper login_user acts on #controller, but I'm getting the error because #controller is nil in my controller spec. Here's my spec:
#spec/controllers/forums_controller_spec.rb
require 'spec_helper'
describe ForumsController do
render_views
describe 'GET new' do
describe 'when guest' do
it 'should deny and redirect' do
get :new
response.should redirect_to(root_path)
end
end
describe 'when admin' do
p #controller #=> nil
#user = User.create!(username: "Test", password: "secret", email: "test#test.com")
login_user # <--------------- where the error occurs
it 'should resolve' do
get :new
response.should render_template(:new)
end
end
end
end
FWIW, I spent a lot of time looking for an answer to this problem. I am using Capybara and RSpec. As it turns out, you need to login manually to using Sorcery to get the login to work.
I've created a Gist on creating integration tests with Sorcery/Rspec/Capybara here:
https://gist.github.com/2359120/9989c14af19a48ba726240d030c414b882b96a8a
You need to include the Sorcery test helpers in your spec_helper
include Sorcery::TestHelpers::Rails
See the sorcery wiki : https://github.com/NoamB/sorcery/wiki/Testing-rails-3
In the example rails app, this is done at https://github.com/NoamB/sorcery-example-app/blob/master/test/test_helper.rb#L13
Updated
Do you have any other Controller specs in the same folder which pass successfully ?
RSpec usually mixes in the required stuff for controller testing for the specs in "spec/controllers" folder.
You could try explicitly marking this as a controller spec by writing
describe ForumsController, :type => :controller do
You need to put your user creation and login into a before(:each) block as follows:
describe 'when admin' do
before(:each) do
#user = User.create!(username: "Test", password: "secret", email: "test#test.com")
login_user
end
it 'should resolve' do
get :new
response.should render_template(:new)
end
end
I've just experienced this dilemma myself and drawing from the input from danneu, diwalak and Birdlevitator (in this thead: rail3/rspec/devise: rspec controller test fails unless I add a dummy=subject.current_user.inspect) I think I can see a solution.
I've been working with a standard rails 3 rspec generated resource from the 'rails generate scaffold' command. Here's the controller rspec file after I modified it to work with a sorcery login:
require 'spec_helper'
# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator. If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails. There are a number
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec. Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.
describe RecordsController do
before(:each) do
#user = User.create!(forename: "Billy", surname: "Bob", username: "Test", password: "secret!1", email: "test#test.com")
login_user
end
# This should return the minimal set of attributes required to create a valid
# Record. As you add validations to Record, be sure to
# update the return value of this method accordingly.
def valid_attributes
{ :owner => 'Mr Blobby', :catagory => 'Index'}
end
# This should return the minimal set of values that should be in the session
# in order to pass any filters (e.g. authentication) defined in
# RecordsController. Be sure to keep this updated too.
def valid_session
{"warden.user.user.key" => session["warden.user.user.key"]}
end
describe "GET index" do
it "assigns all records as #records" do
record = Record.create! valid_attributes
get :index, {}, valid_session
assigns(:records).should eq([record])
end
end
describe "GET show" do
it "assigns the requested record as #record" do
record = Record.create! valid_attributes
get :show, {:id => record.to_param}, valid_session
assigns(:record).should eq(record)
end
end
describe "GET new" do
it "assigns a new record as #record" do
get :new, {}, valid_session
assigns(:record).should be_a_new(Record)
end
end
describe "GET edit" do
it "assigns the requested record as #record" do
record = Record.create! valid_attributes
get :edit, {:id => record.to_param}, valid_session
assigns(:record).should eq(record)
end
end
describe "POST create" do
describe "with valid params" do
it "creates a new Record" do
expect {
post :create, {:record => valid_attributes}, valid_session
}.to change(Record, :count).by(1)
end
it "assigns a newly created record as #record" do
post :create, {:record => valid_attributes}, valid_session
assigns(:record).should be_a(Record)
assigns(:record).should be_persisted
end
it "redirects to the created record" do
post :create, {:record => valid_attributes}, valid_session
response.should redirect_to(Record.last)
end
end
describe "with invalid params" do
it "assigns a newly created but unsaved record as #record" do
# Trigger the behavior that occurs when invalid params are submitted
Record.any_instance.stub(:save).and_return(false)
post :create, {:record => {}}, valid_session
assigns(:record).should be_a_new(Record)
end
it "re-renders the 'new' template" do
# Trigger the behavior that occurs when invalid params are submitted
Record.any_instance.stub(:save).and_return(false)
post :create, {:record => {}}, valid_session
response.should render_template("new")
end
end
end
describe "PUT update" do
describe "with valid params" do
it "updates the requested record" do
record = Record.create! valid_attributes
# Assuming there are no other records in the database, this
# specifies that the Record created on the previous line
# receives the :update_attributes message with whatever params are
# submitted in the request.
Record.any_instance.should_receive(:update_attributes).with({'these' => 'params'})
put :update, {:id => record.to_param, :record => {'these' => 'params'}}, valid_session
end
it "assigns the requested record as #record" do
record = Record.create! valid_attributes
put :update, {:id => record.to_param, :record => valid_attributes}, valid_session
assigns(:record).should eq(record)
end
it "redirects to the record" do
record = Record.create! valid_attributes
put :update, {:id => record.to_param, :record => valid_attributes}, valid_session
response.should redirect_to(record)
end
end
describe "with invalid params" do
it "assigns the record as #record" do
record = Record.create! valid_attributes
# Trigger the behavior that occurs when invalid params are submitted
Record.any_instance.stub(:save).and_return(false)
put :update, {:id => record.to_param, :record => {}}, valid_session
assigns(:record).should eq(record)
end
it "re-renders the 'edit' template" do
record = Record.create! valid_attributes
# Trigger the behavior that occurs when invalid params are submitted
Record.any_instance.stub(:save).and_return(false)
put :update, {:id => record.to_param, :record => {}}, valid_session
response.should render_template("edit")
end
end
end
describe "DELETE destroy" do
it "destroys the requested record" do
record = Record.create! valid_attributes
expect {
delete :destroy, {:id => record.to_param}, valid_session
}.to change(Record, :count).by(-1)
end
it "redirects to the records list" do
record = Record.create! valid_attributes
delete :destroy, {:id => record.to_param}, valid_session
response.should redirect_to(records_url)
end
end
end
And a run down of the important bits:
This bit does the programmatic login (ignore the forename and surname attributes, they're specific to the solution I'm building):
before(:each) do
#user = User.create!(forename: "Billy", surname: "Bob", username: "Test", password: "secret!1", email: "test#test.com")
login_user
end
This bit holds session info/key data:
def valid_session
{"warden.user.user.key" => session["warden.user.user.key"]}
end
As diwalak writes, we need to add this to the spec_help.rb file:
include Sorcery::TestHelpers::Rails
And that's it - worked for me anyhow :)
I'm kind of new to Rails 3.1. and I'm facing an issue only in my production env with my Signup form (actually, it's more about the controller).
Here is the code in User
class UsersController < ApplicationController
[...]
def create
#user = User.new(params[:user])
logger.info "value of login in param : #{params[:user][:login]}" #-> log the actual login
logger.info "value of login : #{#user.login}" #-> log empty
#user.admin = false
if #user.save
flash[:notice] = t('flash.notice.user.create.valid')
redirect_back_or_default root_path
else
flash[:notice] = t('flash.notice.user.create.invalid')
render :action => :new
end
end
end
Also, the controller logs show that the params hash is good
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"QwOqmp0CT/d4mmC1yiLT4uZjP9bNDhbUXHanCQy5ZrA=",
"user"=>{"login"=>"myLogin",
"email"=>"t.r#gmail.com",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"}}
My login form works as expected (already created users are able to sign in)
Again, this only happens in production.
EDIT: Here is my User Model
class User < ActiveRecord::Base
acts_as_authentic
#== Callbacks
before_create :set_defaults
attr_accessible :avatar ##### EDIT: TO FIX THE ISSUE, ADD THE OTHER FIELDS AS WELL
protected
def set_defaults
self.total_1 = self.total_2 = self.total_3 = 0
end
end
Just to memorialize the answer from the comments above:
Normally you can use mass assignment to set fields on a model, but when you use attr_accessible, you are then limited to only mass assigning those fields. So stuff like User.new(params[:user]) won't work; instead, you'd have to do:
#user = User.new
#user.login = params[:user][:login]
# ...etc.
#user.save
Simple add your fields to the attr_accessible list and you can go back to mass assignment.
I've looked into some tutes and all I saw were old posts on how to test before_create. Also it seems like they're all just testing that before_create was called i.e.:
#user = User.new
#user.should_receive(:method_name_called_by_before_create)
#user.send(:before_create) (sometimes they just do #user.save)
I want to actually test that my method worked and that it had assigned(and saved the variables) after creating the record.
Here are my models:
user.rb
class User < ActiveRecord::Base
has_one :character, :dependent => :destroy
after_create :generate_character
private
def generate_character
self.create_character(:name => "#{email}'s avatar")
end
end
and character.rb
class Character < ActiveRecord::Base
belongs_to :user
before_create :generate_character
private
def generate_character
response = api_call
#API CALL HERE
#set object attributes here
self.stat1 = calculate_stat1(response) + 5
self.stat2 = calculate_stat2(response) + 5
self.stat3 = calculate_stat3(response) + 5
end
def api_call
return api_call_response
end
end
I want to test that generate character indeed set the attributes without going online and calling the API call. Is this possible with rspec? I have a fixture of a json response so I was hoping I can stub out generate character and then use the fake response for testing.
Here's my character.spec:
describe Character do
before(:each) do
Character.any_instance.stub!(:api_call).and_return(fake_response.read)
#user = Factory(:user)
#character = #user.character
puts #character.inspect
end
def fake_response
File.open("spec/fixtures/api_response.json")
end
It prints out only 5 for each of the character's stats. Also I did a puts response in the generate_character method in character.rb and it still prints out the "real" api call.
I managed to do a puts in fake_response and it does goes through there but it also goes through the "real" api_call after, which makes the stub obsolete. How do I get through this?
A good approach here is extracting your api call into a self contained method. Something like this:
class Character < ActiveRecord::Base
belongs_to :user
before_create :generate_character
private
def generate_character
data = api_call
#set object attributes from data
end
def api_call
# returns a data structure
# resulting from the call
end
end
Then use RSpec's any_instance to stub the api_call method to return a fixed data structure
Character.any_instance.stub!(:api_call).and_return { {:id => 1, :attribute_one => "foo"} }
#user = User.create
#user.character.attribute_one.should == "foo"
for more info on any_instance check this commit