Hey all,
I’ve got some code that I (mostly) inherited. It essentially has a
couple of AR class methods that look for a specific record by id:
class Project < ActiveRecord::Base
class << self
def specific_project
@another_specific_project ||= Project.find(10) if
Project.exists?(10)
end
def another_specific_project
@specific_project ||= Project.find(11) if Project.exists?(11)
end
end
end
Typically, when I’ve specced this code (or more accurately, code that
uses it), I’ve stubbed out those methods to return a mocked model.
Lately, I’ve started using cucumber and adding stories for areas we’re
adding features to or finding regressions in. From what I can tell, I
can’t stub or mock anything from within cucumber step files. Realizing
that the pattern is a bit of code smell, I feel like I have two
directions I could go:
- Is there a way to stub out the model to return some fixture-type
records?
- Does anyone have an idea as to how we could refactor this into a
better pattern? Those 2 “projects” are pretty specific to the
production data and will “never be edited,” but it still doesn’t make
me comfortable.
I’m not sure how to phrase this better. Let me know what other detail
I can provide to make it more clear what I’m looking for.
thanks,
tim
On Mon, Sep 15, 2008 at 4:14 PM, Tim G. [email protected] wrote:
anything from within cucumber step files. Realizing that the pattern is a
bit of code smell, I feel like I have two directions I could go:
- Is there a way to stub out the model to return some fixture-type records?
I don’t think you’re really supposed to mock or stub when using
cucumber.
- Does anyone have an idea as to how we could refactor this into a better
pattern? Those 2 “projects” are pretty specific to the production data and
will “never be edited,” but it still doesn’t make me comfortable.
Maybe you could add a column that represents the unique characteristic
of the particular projects. I’d probably end up with:
def internal_project
@internal_project ||= Project.find(:first, :conditions => “internal IS
TRUE”)
end
def demo_project
@demo_project ||= Project.find(:first, :conditions => “demo IS TRUE”)
end
or maybe have one column and let its value change:
def internal_project
@internal_project ||= Project.find(:first, :conditions =>
“role=‘internal’”)
end
def demo_project
@demo_project ||= Project.find(:first, :conditions => “role=‘demo’”)
end
Pat
Tim G. wrote:
- Is there a way to stub out the model to return some fixture-type
records?
- Does anyone have an idea as to how we could refactor this into a
better pattern? Those 2 “projects” are pretty specific to the
production data and will “never be edited,” but it still doesn’t make
me comfortable.
I’m not sure how to phrase this better. Let me know what other detail
I can provide to make it more clear what I’m looking for.
thanks,
tim
As Pat said I would avoid mocking/stubbing, you want your cucumber tests
to cut through all the layers of your app including touching the
database.
As for setting up the data, I tend not to use fixtures (I only want to
have the data created for certain tests). Instead I save the relevant
models in the Given steps, preparing for the feature test. If you are
going to repeat the given steps a lot I would extract the model set-up
into a ruby function and reuse this.
–
Joseph W.
http://www.joesniff.co.uk
On Mon, Sep 15, 2008 at 11:01 PM, Joseph W. [email protected]
wrote:
thanks,
tim
As Pat said I would avoid mocking/stubbing, you want your cucumber tests
to cut through all the layers of your app including touching the
database.
I second that.
Aslak
On 15 Sep 2008, at 21:14, Tim G. wrote:
end
areas we’re adding features to or finding regressions in. From what
I can tell, I can’t stub or mock anything from within cucumber step
files. Realizing that the pattern is a bit of code smell, I feel
like I have two directions I could go:
- Is there a way to stub out the model to return some fixture-type
records?
- Does anyone have an idea as to how we could refactor this into a
better pattern? Those 2 “projects” are pretty specific to the
production data and will “never be edited,” but it still doesn’t
make me comfortable.
If those objects are built into your system and will never, ever
change, I would consider storing their definition in the code rather
than in the database anyway.
http://www.refactoring.com/catalog/replaceTypeCodeWithSubclasses.html
That would get around your issues with the pristine test database
being different to the production / development database, and IMO
more clearly communicates to future developers that these objects are
‘special’.
Of course it depends on how many of them there are, whether you have
a use case for editing them etc, but it’s worth thinking about.
Alternatively you could call the migrations that insert this stock
data (presumably you have some) from your spec pre-requisites.
cheers,
Matt
http://blog.mattwynne.net
In case you wondered: The opinions expressed in this email are my own
and do not necessarily reflect the views of any former, current or
future employers of mine.
On Mon, Sep 15, 2008 at 4:01 PM, Joseph W. [email protected]
wrote:
going to repeat the given steps a lot I would extract the model set-up
into a ruby function and reuse this.
You might also take a look at GitHub - flogic/object_daddy: Who's your daddy? Kill Rails fixtures, Don't Repeat Yourself, reduce the complexity of your tests..
Cheers,
David
def another_specific_project
files. Realizing that the pattern is a bit of code smell, I feel
than in the database anyway.
Replace Type Code with Subclasses
That would get around your issues with the pristine test database
being different to the production / development database, and IMO
more clearly communicates to future developers that these objects
are ‘special’.
Of course it depends on how many of them there are, whether you have
a use case for editing them etc, but it’s worth thinking about.
Thanks all for the ideas. I knew that stubbing or mocking from within
Cucumber was the wrong direction. I started exploring going that
direction, but all my instincts were crying out against it.
I’m going to look into either subclassing the Project model or putting
the differences in the database itself. I’ve considered putting the
differences into the data previously, but we’re talking about 2
distinct projects out of 100+. I would need 2 new columns for the data
and they would only ever each be used for 1 project - doesn’t feel
right somehow.
Matt - in terms of subclassing it, I have the entire stable of
projects, 1 “internal” project and 1 “slush fund” project. If I’m
subclassing the project, I assume that I still need to have a record
for them in the database that needs to be findable. So while the
subclass suggestion helps, I’m not sure it gets me all the way there?
Could very well be that I’m missing something there…
As for setting up the data, I tend not to use fixtures (I only want
to
have the data created for certain tests). Instead I save the relevant
models in the Given steps, preparing for the feature test. If you are
going to repeat the given steps a lot I would extract the model set-
up
into a ruby function and reuse this.
You might also take a look at GitHub - flogic/object_daddy: Who's your daddy? Kill Rails fixtures, Don't Repeat Yourself, reduce the complexity of your tests..
For test data, I’ve been using the FixtureReplacement plugin rather
than fixtures - it basically abstracts the creation and destruction of
objects for every scenario. However, I’ve only been using it for about
a week now. It works well, but I’m not married to it yet by any
stretch. Is Object Daddy in a stable state? it looks pretty tasty, I
have to say.
thanks again,
tim
On Sep 15, 2008, at 4:14 PM, Tim G. wrote:
end
def another_specific_project
@specific_project ||= Project.find(11) if Project.exists?(11)
end
end
end
Why don’t use just use a slug for these things? In other words - use a
unique name for each record, since the numbers 10 and 11 don’t mean
much to anyone.
Then, use a factory (like FixtureReplacement or Object Daddy) to
generate the records in env.rb. Here’s how you’d do that with
FixtureReplacement (at the bottom of your env.rb file):
include FixtureReplacement
create_project(:slug => “my-unique-name-for-project1”)
create_project(:slug => “my-unique-name-for-the-second-project”)
Scott
On 16 Sep 2008, at 15:41, Tim G. wrote:
Matt - in terms of subclassing it, I have the entire stable of
projects, 1 “internal” project and 1 “slush fund” project. If I’m
subclassing the project, I assume that I still need to have a
record for them in the database that needs to be findable. So while
the subclass suggestion helps, I’m not sure it gets me all the way
there? Could very well be that I’m missing something there…
Yeah this is where ActiveRecord’s tight coupling to databases really
starts to bite. You could override all the Project.find calls and
merge in the two stock, hard-coded projects (not subclasses) into
every resultset but that sounds like quite hard work.
You could also use STI maybe, and store a row for the two stock
projects but only store the type and put everything else in code. If
you only want one of each though, I don’t think that refactoring to
subclasses is really appropriate. You more want to be able to call
Project.internal or Project.slush_fund and always get back that
singleton instance of Project…
I’ve never needed to do this yet in ruby / rails so you may notice
I’m becoming rather hand-wavey at this point. I think I’ll get my
coat…
cheers,
Matt
http://blog.mattwynne.net
In case you wondered: The opinions expressed in this email are my own
and do not necessarily reflect the views of any former, current or
future employers of mine.
create_project(:slug => “my-unique-name-for-project1”)
create_project(:slug => “my-unique-name-for-the-second-project”)
Yah - I actually did a similar thing for another model I found with
the same issue. This way I only have to add one column to the db. For
now, those are the only 2 projects we would need a slug for but, if we
needed more, we could add them later.
thanks for the logical conclusion to my issue
On Sep 15, 2008, at 4:35 PM, Pat M. wrote:
I don’t think you’re really supposed to mock or stub when using
cucumber.
We need to stub time in some of our scenarios, which exist to to
verify behavior over time. We’re looking into a before/after to
support mocking/stubbing for this scenario.
Cheers,
Luke
Luke M.
[email protected]
On Tue, Sep 30, 2008 at 9:55 AM, Luke M. [email protected] wrote:
On Sep 15, 2008, at 4:35 PM, Pat M. wrote:
I don’t think you’re really supposed to mock or stub when using cucumber.
We need to stub time in some of our scenarios, which exist to to verify
behavior over time. We’re looking into a before/after to support
mocking/stubbing for this scenario.
There’s no direct hooks into the mock framework and I don’t think
there should be, but you can roll your own in the supplied
Before/After methods:
Before do
#do some magic w/ time
end
After do
#undo some magic w/ time
end
Those run before and after every scenario.
On Tue, Sep 30, 2008 at 10:26 AM, Scott T.
[email protected] wrote:
We need to stub time in some of our scenarios, which exist to to verify
After do
#undo some magic w/ time
end
Those run before and after every scenario.
How would you do this? I guess you could just require spec/mocks/mock, mock
/ stub as usual, and then in the After block call
Spec::Mocks::Space#reset_all ?
That could work. And actually, I’d be OK w/ the idea of stubs, just
not message expectations (mocks).
Aslak?
Have you thought about building a fake object instead of using
mocking / stubbing? It might be simpler, and you can use Pat’s
ServiceLocator idea to substitute your fake for the cucumber runs.
On Sep 30, 2008, at 11:06 AM, David C. wrote:
mocking/stubbing for this scenario.
#undo some magic w/ time
end
Those run before and after every scenario.
How would you do this? I guess you could just require spec/mocks/
mock, mock / stub as usual, and then in the After block call
Spec::Mocks::Space#reset_all ?
Scott
Scott T. [email protected] writes:
verify
After do
#undo some magic w/ time
end
Those run before and after every scenario.
How would you do this? I guess you could just require spec/mocks/
mock, mock / stub as usual, and then in the After block call
Spec::Mocks::Space#reset_all ?
You can do magic without mocks…
orig_now = Time.method(:now)
Before do
now = Time.now
(class << Time; self; end).send(:define_method, :now) { now }
end
After do
(class << Time; self; end).send(:define_method, :now) { orig_now.call
}
end
Pat
On Sep 30, 2008, at 12:04 PM, Pat M. wrote:
#do some magic w/ time
Spec::Mocks::Space#reset_all ?
(class << Time; self; end).send(:define_method, :now)
{ orig_now.call }
end
Sure. If our mocks were macros, wouldn’t it expand into this code?
Scott
Subject says most of it. I’d love to use Cucumber in my project but I
need to be able to install it in a Rails app and by a particular
version number.
I forked it and struggled with getting GitHub gems deployer to behave
itself.
Maybe a “canonical” version can be kept and updated in RubyForge
occasionally because of occasional gem problems with GitHub?
Evan
On Tue, Sep 30, 2008 at 9:02 PM, Evan David L.
[email protected] wrote:
Subject says most of it. I’d love to use Cucumber in my project but I need
to be able to install it in a Rails app and by a particular version number.
You can do that with git pull and git checkout. Would it help if
detailed instructions were posted to the wiki?
I forked it and struggled with getting GitHub gems deployer to behave
itself.
Maybe a “canonical” version can be kept and updated in RubyForge
occasionally because of occasional gem problems with GitHub?
Yes, I’ll probably do that soon. GitHub fails to build the gem every
time for me (not occasionally).
http://logicalawesome.lighthouseapp.com/projects/8570-github/tickets/945
Aslak
On Sep 30, 2008, at 5:20 PM, aslak hellesoy wrote:
Not so much. I can get the repo, build, and install the gem. I’m
trying to make it easier for members of my team who get a copy of our
repo but need to install the dependent gems.
Maybe a “canonical” version can be kept and updated in RubyForge
occasionally because of occasional gem problems with GitHub?
Yes, I’ll probably do that soon. GitHub fails to build the gem every
time for me (not occasionally).
http://logicalawesome.lighthouseapp.com/projects/8570-github/tickets/945
It’s worse than that for me: it keeps disabling the “Rubygems” setting
in the Admin screen.
I lose a little bit of love for GitHub over such an important feature
working incorrectly.
Evan