Skip to content

Instantly share code, notes, and snippets.

@dchelimsky
Forked from ryanb/1_let.rb
Created March 29, 2012 23:33
Show Gist options
  • Save dchelimsky/2244919 to your computer and use it in GitHub Desktop.
Save dchelimsky/2244919 to your computer and use it in GitHub Desktop.
let vs def
desc "A user's comment" do
let(:user) { User.create! name: "John" }
let(:comment) { user.comments.create! }
it "delegates to user's name" do
comment.name.should eq(user.name)
end
end
desc "A user's comment" do
def user
@user ||= User.create! name: "John"
end
def comment
@comment ||= user.comments.create!
end
it "delegates to user's name" do
comment.name.should eq(user.name)
end
end
desc "A user's comment" do
def user; @user ||= User.create! name: "John"; end
def comment; @comment ||= user.comments.create!; end
it "delegates to user's name" do
comment.name.should eq(user.name)
end
end
# the original motivation for `let` was to make this scenario easier:
# step 1
describe Thing do
it "does something" do
thing = Thing.new
thing.should do_something
end
end
# step 2 - add another example
describe Thing do
it "does something" do
thing = Thing.new
thing.should do_something
end
it "does something else" do
thing = Thing.new
thing.should do_something_else
end
end
# step 3 - refactor - without let
# 3a1
describe Thing do
before do
@thing = Thing.new
end
it "does something" do
thing = Thing.new
thing.should do_something
end
it "does something else" do
thing = Thing.new
thing.should do_something_else
end
end
# 3a2
describe Thing do
before do
@thing = Thing.new
end
it "does something" do
thing.should do_something
end
it "does something else" do
thing = Thing.new
thing.should do_something_else
end
end
# 3a3
describe Thing do
before do
@thing = Thing.new
end
it "does something" do
@thing.should do_something
end
it "does something else" do
thing = Thing.new
thing.should do_something_else
end
end
# 3a4
describe Thing do
before do
@thing = Thing.new
end
it "does something" do
@thing.should do_something
end
it "does something else" do
thing.should do_something_else
end
end
# 3a5
describe Thing do
before do
@thing = Thing.new
end
it "does something" do
@thing.should do_something
end
it "does something else" do
@thing.should do_something_else
end
end
# and now with let - fewer steps, no adding @ signs
# 3b1
describe Thing do
let(:thing) { Thing.new }
it "does something" do
thing = Thing.new
thing.should do_something
end
it "does something else" do
thing = Thing.new
thing.should do_something_else
end
end
# 3b2
describe Thing do
let(:thing) { Thing.new }
it "does something" do
thing.should do_something
end
it "does something else" do
thing = Thing.new
thing.should do_something_else
end
end
# 3b2
describe Thing do
let(:thing) { Thing.new }
it "does something" do
thing.should do_something
end
it "does something else" do
thing.should do_something_else
end
end
@tenderlove
Copy link

Why not just use attr_reader? Then skip the "add @" step (which is how I would refactor a "normal class)?

@dchelimsky
Copy link
Author

If that makes you happy you should do that :) TBH I find myself favoring local assignment more and more.

@dchelimsky
Copy link
Author

Also using attr_reader means you still have to declare the ivar in a before hook (or setup if that's how you roll). let is therefore less code to write.

@tenderlove
Copy link

lol! Yes, it's how I roll. ;-)

let may be fewer lines, but it also contains a conditional statement. The setup / attr_reader solution has no conditionals. One other downside is that (I'm guessing) the ||= isn't wrapped in a mutex, so there could be threading issues (were the tests to be run in multiple threads).

@dchelimsky
Copy link
Author

let is memorized per example, so I don't know where you'd see thread safety issues.

@tenderlove
Copy link

tenderlove commented Mar 30, 2012 via email

@dchelimsky
Copy link
Author

@tenderlove - you're so filled with possibilities :) Agree that this is a potential pitfall that both libs should probably warn about in docs.

@tenderlove
Copy link

TEE HEE! I love to be possible! :-D

@tenderlove
Copy link

Awww! Thanks! :-D

You're the best! :-D ❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️

@dchelimsky
Copy link
Author

This also inspired me to add some docs about the oft-abused subject: http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/Subject/ExampleMethods#subject-instance_method

@croaky
Copy link

croaky commented Apr 16, 2012

I find 3a5 the most readable and consistent with the Four-Phase Test. Not sure I understand the gains of the memoization and agree with @tenderlove that the hidden conditional is a little freaky.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment