I started out learning how to mock things because using Factory Girl just isn't very practical for projects with 1000+ tests. I can't be hitting the database for every test, especially if I hope to do any sort of automated continuous integration.
My Test:
it "should return an URL with the desired security protocol" do
p = Proposal.new
p.expects(:status).returns(Proposal::PUBLISHED) #this should be invoked by public_url?
p.expects(:public_url).returns("https://something")
p.expects(:public_url).with(true).returns("https://something")
p.expects(:public_url).with(false).returns("http://something")
assert p.public_url.index("https") != nil
assert p.public_url(true).index("https") != nil
assert p.public_url(false).index("https") == nil
end
The method for the above test:
def public_url(https = true)
url = ""
if self.published?
# attepmt to find sluggable id
id = sluggable_id
url = (https ? "#{ENV['SSL_PROTOCOL']}://" : "http://") +
self.account.full_domain + "/view/" + id.to_s
end
return url
end
def published?
return self.status > UNPUBLISHED
end
But this is what I get when I run the test:
unsatisfied expectations:
- expected exactly once, not yet invoked: #<Proposal:0x7fbd07e82a30>.status(any_parameters)
Shouldn't the call to public_url
invoke status()
?
If not, then if I have to call p.status
myself, doesn't that mean that p.public_url()
is completetly ignoring the logic I have written, and strictly following what I have defined in expects
?
How does that help with unit testing?
Maybe I don't understand the purpose of mocking.
UPDATE:
I changed my test to this, as per @Philip's suggestion, removing the need for any ActiveRecord shenanigans:
it "should return an URL with the desired security protocol" do
p = Proposal.new
p.expects(:id).returns(1)
p.expects(:status).returns(Proposal::PUBLISHED)
p.expects(:account).returns(Account.new.stubs(:localhost))
assert p.public_url.starts_with("https")
assert p.public_url(true).starts_with("https")
assert !p.public_url(false).starts_with("https")
end
I guess now my question is, how do I stub out an account, using a fixture (that I have named localhost
?). I get an error: undefined method 'full_domain' for Mocha::Expectation:
but my fixture is defined as follows:
localhost:
id: 1
name: My first account
full_domain: test.domain.vhost
I set up fixtures such that I could have the very basics of commonly accessed models for easy use throughout all of my tests. The Standard Model/Relationship for EVERY test (if I were do the testing by hand/with factory girl with no mocking, would require this:
Account
, Subscription
(has Account
, SubscriptionPlan
), SubscriptionPlan
, User
(belongs to Account
), AccountProperties
(has Account
), and then the actual Object I'm testing on, which belongs to an Account
and a User
on that Account
, which is a bit much for every test. haha
UPDATE 2:
Got it working:
it "should return an URL with the desired security protocol" do
p = Proposal.new({:status => Proposal::PUBLISHED})
p.expects(:id).returns(1)
p.expects(:account).returns(accounts(:localhost)).times(3)
assert p.public_url.starts_with?("https")
assert p.public_url(true).starts_with?("https")
assert !p.public_url(false).starts_with?("https")
end
turns out, you access fixtures like, accounts
or users
.