In my unit tests I sometimes mock Rails classes to avoid a complicated
test setup. For instance, by redefining ActiveRecord::Base#find I can
run tests without a database being involved.
Tests like that work nicely by themselves when run by (plain) ruby or
testrb. Trouble strikes when I try to run them from a rake task in
conjunction with other tests. Most tests, of course, expect Rails
classes to work as usual. Thus, what I need is a way to restrict my
changes to the tests in one file. Well, it''s not possible to actually
restrict it, but it is possible to revert it when it is no longer
needed. That''s what the combo of setup and teardown can be used for.
See below for a demonstration of the technique.
Michael
----8><----8><----8><----8><----8><----8><----8><
$:.unshift File.join(File.dirname(__FILE__))
require ''test_helper''
require ''active_support''
require ''boilerplate/enumerations''
class ActiveRecord::Base
cattr_accessor :find_result, :expect_find
def self.mock_find(*args)
fail "unexpected call to find" unless @@expect_find > 0
@@expect_find -= 1
find_result
end
end
class EnumerationsTest < Test::Unit::TestCase
DummyEnum = Struct.new(:name, :id)
def setup
ActiveRecord::Base.class_eval <<-END
class <<self
alias_method :original_find, :find
alias_method :find, :mock_find
end
END
expect_find(0)
BoilerPlate::Enumerations::define_enums(
''SimpleEnum''
)
SimpleEnum.reset
end
def teardown
ActiveRecord::Base.class_eval <<-END
class <<self
alias_method :find, :original_find
end
END
end
def expect_find(count = 1)
ActiveRecord::Base::expect_find = count
end
def set_find_result(result)
ActiveRecord::Base::find_result = result.dup
end
def test_all
expect_find
set_find_result [1, 2, 3]
assert_equal(
[1, 2, 3],
SimpleEnum.all)
end
def test_all_should_cache
expect_find
set_find_result [1, 2, 3]
assert_equal(
[1, 2, 3],
SimpleEnum.all)
# no further call to find
assert_equal(
[1, 2, 3],
SimpleEnum.all)
end
### snipped
end
--
Michael Schuerig Nothing is as brilliantly adaptive
mailto:michael-q5aiKMLteq4b1SvskN2V4Q@public.gmane.org as selective
stupidity.
http://www.schuerig.de/michael/ --A.O. Rorty, The Deceptive Self
Thanks Michael, that''s immensely handy. It feels like there aren''t too many people interested in unit testing in this way on this list. Perhaps those of us who are doing a fairly ''kent beck'' style TDD with mock objects and no database should get together for an online chat about techniques some time. Craig
We''re attempting TDD on Rails for our XP codefests in Columbia MD. ( http://agilemaryland.org/moin/CodeProject_2fMeetingSchedule)<http://agilemaryland.org/moin/CodeProject_2fMeetingSchedule>So we''re very interested in the challenges and solutions in doing this. One thing that has been bothering me is running generators or doing DB schema changes before writting a unit test. I''d like to see a unit test fail, run the generator, see unit test pass. Any advice on this? On 8/22/05, Craig Ambrose <craig-sZi9aYDroxztFRKb8DbDFhCuuivNXqWP@public.gmane.org> wrote:> > > Thanks Michael, that''s immensely handy. It feels like there aren''t too > many people interested in unit testing in this way on this list. Perhaps > those of us who are doing a fairly ''kent beck'' style TDD with mock > objects and no database should get together for an online chat about > techniques some time. > > Craig > > > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-- It is the duty of a patriot to protect his country from its government" -Thomas Paine Blog: http://superwombat.blogspot.com Bookmarks: http://del.icio.us/jeffwaltzer _______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
I really like not having to use mockobjects but my tests are getting out of hand as far as run time speed is concerned, almost to the point where I avoid running them because I don''t get that instant feedback when I would use mockobjects in javaland, my rails tests are now taking about 40+ seconds to run on my main devel platform a 1.5ghz powerbook anyhow, how far do you plan on taking this? would be nice if I could just mock out all of AR (blindly assuming that AR/DB is the bulk of the speed hit) On 8/22/05, Michael Schuerig <michael-q5aiKMLteq4b1SvskN2V4Q@public.gmane.org> wrote:> > > In my unit tests I sometimes mock Rails classes to avoid a complicated > test setup. For instance, by redefining ActiveRecord::Base#find I can > run tests without a database being involved. > > Tests like that work nicely by themselves when run by (plain) ruby or > testrb. Trouble strikes when I try to run them from a rake task in > conjunction with other tests. Most tests, of course, expect Rails > classes to work as usual. Thus, what I need is a way to restrict my > changes to the tests in one file. Well, it''s not possible to actually > restrict it, but it is possible to revert it when it is no longer > needed. That''s what the combo of setup and teardown can be used for. > See below for a demonstration of the technique. > > Michael > > > ----8><----8><----8><----8><----8><----8><----8>< > > $:.unshift File.join(File.dirname(__FILE__)) > > require ''test_helper'' > require ''active_support'' > require ''boilerplate/enumerations'' > > > class ActiveRecord::Base > cattr_accessor :find_result, :expect_find > def self.mock_find(*args) > fail "unexpected call to find" unless @@expect_find > 0 > @@expect_find -= 1 > find_result > end > end > > class EnumerationsTest < Test::Unit::TestCase > > DummyEnum = Struct.new(:name, :id) > > def setup > ActiveRecord::Base.class_eval <<-END > class <<self > alias_method :original_find, :find > alias_method :find, :mock_find > end > END > > expect_find(0) > BoilerPlate::Enumerations::define_enums( > ''SimpleEnum'' > ) > SimpleEnum.reset > end > > def teardown > ActiveRecord::Base.class_eval <<-END > class <<self > alias_method :find, :original_find > end > END > end > > def expect_find(count = 1) > ActiveRecord::Base::expect_find = count > end > > def set_find_result(result) > ActiveRecord::Base::find_result = result.dup > end > > > def test_all > expect_find > set_find_result [1, 2, 3] > assert_equal( > [1, 2, 3], > SimpleEnum.all) > end > > def test_all_should_cache > expect_find > set_find_result [1, 2, 3] > assert_equal( > [1, 2, 3], > SimpleEnum.all) > # no further call to find > assert_equal( > [1, 2, 3], > SimpleEnum.all) > end > > ### snipped > > end > > -- > Michael Schuerig Nothing is as brilliantly adaptive > mailto:michael-q5aiKMLteq4b1SvskN2V4Q@public.gmane.org as selective stupidity. > http://www.schuerig.de/michael/ --A.O. Rorty, The Deceptive Self > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >_______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
Sounds interesting Jeff. Just to get pedantic, it doesn''t really sound like a unit test if it''s testing the effect of generators on the code and database. More like an integration or acceptance test. However you could no doubt do that with Ruby::Test::Unit, but you''d probably need to do some sort of scripting over the top to make it happen. If you get specific about your example, I''d be happy to give you my thoughts on how to do it (such as they are). Craig Jeff Waltzer wrote:> We''re attempting TDD on Rails for our XP codefests in Columbia MD. > (http://agilemaryland.org/moin/CodeProject_2fMeetingSchedule) > <http://agilemaryland.org/moin/CodeProject_2fMeetingSchedule> So we''re > very interested in the challenges and solutions in doing this. One > thing that has been bothering me is running generators or doing DB > schema changes before writting a unit test. I''d like to see a unit > test fail, run the generator, see unit test pass. Any advice on this?
Hi Francisco,
Further to Michael''s orginal post on this, I''ve added some
addtional
thoughts (and modified code) here:
http://groups.yahoo.com/group/xp_on_rails/message/1
regards,
Craig
Francisco Hernandez wrote:
> I really like not having to use mockobjects but my tests are getting
> out of hand as far as run time speed is concerned, almost to the point
> where I avoid running them because I don''t get that instant
feedback
> when I would use mockobjects in javaland, my rails tests are now
> taking about 40+ seconds to run on my main devel platform a 1.5ghz
> powerbook
>
> anyhow, how far do you plan on taking this? would be nice if I could
> just mock out all of AR (blindly assuming that AR/DB is the bulk of
> the speed hit)
On Monday 05 September 2005 00:18, Francisco Hernandez wrote:> anyhow, how far do you plan on taking this? would be nice if I could > just mock out all of AR (blindly assuming that AR/DB is the bulk of > the speed hit)I don''t plan anything, I just mock things up as I go. Michael -- Michael Schuerig Life is what happens mailto:michael-q5aiKMLteq4b1SvskN2V4Q@public.gmane.org While you''re making plans http://www.schuerig.de/michael/ --Kevin Gilbert, A Long Day''s Life