Reputation: 407
I'm running an integration test in rspec and the test keeps throwing up an undefined method on billed_for
:
"undefined method billed_for
nil:NilClass"
require 'user'
describe "Integration" do
let(:user) { User.new(voucher) }
context 'no voucher' do
let(:voucher) { nil }
it 'should bill default price all the time' do
user.bill
expect(user.orders[0].billed_for).to eql 6.95
end
end
end
I have a very small user class so far
require 'order'
require 'voucher'
class User
attr_accessor :voucher, :orders
def initialize(orders = [], voucher = nil)
@voucher = voucher
@orders = [orders]
end
def bill
new_order = Order.new(self)
@orders << new_order
end
end
and an equally small order class:
class Order
DEFAULT_PRICE = 6.95
attr_accessor :user
def initialize(user)
@user = user
end
def billed_for
price = DEFAULT_PRICE
user.orders.each do |order|
price - order.billed_for
end
price
end
end
What's confusing me most is this line
user.orders[0].billed_for
when I think it through a new user class is set up this let, I then access the orders hash in user hash and then I'm accessing the billed_for
method within the order class.
When I've googled this issue it's pointed towards using the self keyword that isn't working.
If someone could point me in the right direction it'd be great
EDIT:
Jakob S kindly pointed at that my test was failing because of nil entries in my array.
A quick an dirty fix for this was just to run the compact function to remove the nil entry.
Always open to better solutions of course.
EDIT 2:
let(:user) { User.new(voucher) }
context 'no voucher' do
let(:voucher) { nil }
it 'should bill default price all the time' do
user.bill
expect(user.orders[0].billed_for).to eql 6.95
... ...
end
end
context 'vouchers' do
describe 'default vouchers' do
let(:voucher) { Voucher.create(:default, credit: 15) }
it 'should not bill user if has a remaining credit' do
user.bill
expect(user.orders[0].billed_for).to eql 0.0
... ...
end
end
Thanks for the help so far. I've also opened an additional thread as I had a few other similar questions
Accessing variables of other classes
Upvotes: 1
Views: 3205
Reputation: 20135
When you instantiate your user
, you use
let(:user) { User.new(voucher) }
voucher
is defined as nil
in
let(:voucher) { nil }
In other words you instantiate your user
variable with User.new(nil)
.
Your User
constructor has the signature
def initialize(orders = [], voucher = nil)
so by doing User.new(nil)
you're setting the orders
argument to nil
(voucher
is also nil
, but that's by default). Your constructor then goes ahead and creates an instance variable, @orders
that it sets to [orders]
- which in this case is the same as [nil]
.
Your test then goes ahead and adds a new order to the @orders
Array, which is fine, and that leaves your @orders
array containing [nil, instance_of(Order)]
.
Finally, the test tries to send the billed_for
method to the first elements in the orders
array: user.orders[0].billed_for
. The orders array contains [nil, instance_of(Order)]
, the first element of that is nil
, thus you're actually calling
nil.billed_for
in your spec, which results in the error you're seeing.
I think you might get a bit closer to what you're looking for by not passing the voucher
to the orders
argument when instantiating the User
. Also your test might want to check the last element, ie user.orders.last
rather than user.orders[0]
. And I suspect you might stumble across a few more improvements as you go along.
Upvotes: 5