Problem:
- My specs are slow oh no!
- Setting up objects/object trees takes heaps of time
- There's a *lot* of duplication in object creation in most test suites
Solution:
- Add 'smarts' to machinist* so that it auto-generates fixtures for
common objects and keeps them in the DB
Downside:
- You can't rely on your DB being 'clean'
... but if you're using should change_by and friends that often
won't be an issue
- It doesn't actually exist and may even be impossible
Idea:
- Either some sort of profiling, where machinist records object creation
and builds up a library (so first run will be just like normal, second
run will be optimized)
it 'a' do
user = User.make
end
it 'b' do
user = User.make
end
it 'c' do
user = [User.make, User.make]
end
This spec should only require 2 creations, not 4. This example would be
trivial to implement - keep track of how many users are created in each
example, then before all the transactional stuff for the next suite run,
create a few users that can be used (fixtures) rather than creating new
ones. You can have smarts so that if a particular object is only needed
once or twice, then just create it on the fly rather than
pre-generating. For more complex cases may need some 'hints' or what
what for it to work. Have to come up with more examples.
I (and I know a few others) use a before(:all_transactional) hook to
fake this behavior in some of our specs - if object creation takes 1s
and you want to spec 15 attributes, that's a 15x speed up.
I imagine this would subtley change how you write specs, so may not be
able to be retrofitted on to big projects - but I reckon this could be a
big win for large functional suites.
Discuss! Already done it? Handy idea? What examples break it?
Xav
* where ever I say machinist just insert "some sort of object factory thing"
> In which I wave my hands a lot. I haven't thought this through totally, but thought I'd throw it out there.
>
> Problem:
> - My specs are slow oh no!
> - Setting up objects/object trees takes heaps of time
> - There's a *lot* of duplication in object creation in most test suites
I was thinking about this exact problem just this afternoon. I've been writing a few specs for a project that has these same speed issues. Single specs taking 2-3 seconds to run :)
> Solution:
> - Add 'smarts' to machinist* so that it auto-generates fixtures for common objects and keeps them in the DB
I like this idea. I think it's doable.
A pool of fixture records, which specs can "check out" for use. If the pool dries up, machinist lazily creates more.
Time for a hack night? :)
—ben_h
Would love to see it happen, though I'm afraid I don't have time atm
to help.
--
Pat
> --
> You received this message because you are subscribed to the Google
> Groups "Ruby or Rails Oceania" group.
> To post to this group, send email to rails-...@googlegroups.com.
> To unsubscribe from this group, send email to rails-oceani...@googlegroups.com
> .
> For more options, visit this group at http://groups.google.com/group/rails-oceania?hl=en
> .
>
*hands waving* Buuut, say I was to bite... an easy win would be to
take a snapshot at the end of a setup block. In theory, that's all
your duplicated creations done with, and each test is then doing
something unique?
Alex.
Now that would be cool! But I have a question first:
Has anyone done any profiling on their Rails app tests to work out
where the time goes? I've always wondered how much of the time testing
Rails apps is wasted on the database syncing disk writes, etc. If you
threw your test database on a ramdisk, how much performance would you
gain?
- Pete
Has anyone done any profiling on their Rails app tests to work out
where the time goes? I've always wondered how much of the time testing
Rails apps is wasted on the database syncing disk writes, etc. If you
threw your test database on a ramdisk, how much performance would you
gain?
Has anyone done any profiling on their Rails app tests to work out
where the time goes? I've always wondered how much of the time testing
Rails apps is wasted on the database syncing disk writes, etc. If you
threw your test database on a ramdisk, how much performance would you
gain?
I've tried using ruby-prof with rspec a number of times - both to test
spec speed, and to assert things about app speed as well and it was
very painful.
Because of the way rspec builds up it's sets of examples and
everything you end up with a very muddy call tree profile it's really
tricky to get anything meaningful out of it.
My next avenue of exploration (when i get some free time) will to open
up rspec and add hooks to start stop ruby-prof at will, so it will
only show me my own example code rather than all that rspec guff.
-jb
In a new app, each object creation is adding about 0.02s (sounds tiny
buts add up quick).
As a percentage these numbers are far bigger than most other operations
in our suite (we do have some app tasks that take a while, but we know
where those are).
If it's not solved before then, anyone fancy spending some time at
Railscamp working on making this happen?
- Pete
I've used sqlite :memory: in the past when using datamapper and found it to greatly increase the runtime speed for the testing environment.
Surely this would prove greater gains than re-thinking machinst?
Of course, I don't want to get in the way of your hacking project.
The Mysql in-memory database is pretty basic; sort of like MyISAM, but
with even fewer features. To me it sort of defeats most of the
purposes of testing to test against a completely different database
engine (that doesn't even try to support the same features). I think
some time ago (somewhere else) there was mention of another database
engine that could do in-memory stuff, I can't remember what it is.
I wouldn't say it means the idea of in-memory databases is a bad one
unless you're leaning a lot of your logic against the DB as opposed to
relying on a ORM. If you don't, then you can have a production system
running on PostgreSQL, in-memory SQLite databases for tests, and so
on.
--
James
Point taken regarding the SSDs, but RAM is still faster (perhaps not
meaningfully so, unless your test suite is ridiculously large).
http://github.com/xaviershay/machinist/tree/smart-fixtures
Ben H and I had a crack this evening. Totally Not Done. So broken on any
real project we have. However!
It works for:
before(:suite) do
Machinist.use_smart_fixtures = true
end
# Only two INSERT statement will be generated
100.times do |x|
it("test #{x}") { User.make }
end
after(:each) do
Machinist.smart_fixture_teardown!
end
...and we've pushed dinner back about as far as we can stand.
This version builds up the fixtures as the test suite runs, we also
tried one that profiled the app and dumped out fixture data to a file
but that was more complicated. See commit history if you're interested.
Xav + Ben
Hope you're at the Ruby meet tonight so we can chat about it!
- Pete