From: ara.t.howard@... Date: 2007-01-04T01:04:48+09:00 Subject: Re: Problem in Unit Testing Methods that start new threads On Wed, 3 Jan 2007, Hemant Kumar wrote: > I have a bit of doubt, in Unit Testing Programs that start new threads. > Please have a look at the code below: > > class Foobar > > def hello_world > p "Hello World" > @thread_status = false > end > > def new_thread_start > Thread.new do > sleep(100) > @thread_status = true > end > end > > end > > # here goes the lame test case > require "test/unit" > > module Test::Unit::Assertions > def assert_false(t_object,message=nil) > boolean = !t_object > full_message = build_message(message,' Object is not > false',boolean) > assert_block(full_message) { boolean} > end > end > > class TestFoobar < Test::Unit::TestCase > def setup > @foo = Foobar.new > class << @foo > def ivar var > instance_variable_get(:"@#{var}") > end > end > end > > def test_hello_world > @foo.hello_world > assert_false @foo.ivar(:thread_status) > end > > > def test_new_thread > # sorry for a bit of not so DRY thingy > @foo.hello_world > assert_false @foo.ivar(:thread_status) > @foo.new_thread_start > > # next assert is true because method was started in a new thread > # and control came back immediately, what i would probably want is > # to wait here so that i can have proper check on the method and > # state of the program, but i am not sure, if that's exactly > # approach i should take. > > assert_false @foo.ivar(:thread_status) > end > end > > > Now, as someone suggested on IRC, I can do a join and wait for the > thread to finish. But the problem is, I don't exactly have an instance > to the thread, because its managed by a plugin and i am just using the > plugin to do stuff. > > Any ideas/suggestions are more than welcome. it seems that all you've managed to do is write a very long race condition. i'm not one of those people who think the mere mention of the word 'unit-testing' bestows any sort of robustness on code. for example, the testing of 'thread_status' in your test means nothing, as it's the source of the race condition: you need to wrap setting and reading this var with a semaphore. this shows why: harp:~ > cat a.rb class C attr :thread def initialize @thread = nil end def new_thread Thread.new{ @thread = Thread.current } end end 4242.times{|i| raise "race condition @ loopno #{ i }!" unless((c = C.new) and (Thread === c.new_thread) and c.thread) } harp:~ > ruby a.rb a.rb:11: race condition @ loopno 942! (RuntimeError) from a.rb:11 regarding your specific question though, you need to verify that a thread is created and that it's status is running. even if you don't have a handle on the thread you can set things up in your unit test to get one. something like harp:~ > cat a.rb def tracking_threads &b before = Thread.list yield after = Thread.list return after - before end threads = tracking_threads{ 2.times{ Thread.new{ sleep } } } threads.each{|t| p t.status} harp:~ > ruby a.rb "sleep" "sleep" cool. now we've managed to shows that Thread.new works! ;-) i wouldn't bother with this at all unless i could also test that the created thread did the right thing. my 2 cts. kind regards. -a -- if you find yourself slandering anybody, first imagine that your mouth is filled with excrement. it will break you of the habit quickly enough. - the dalai lama