Sidekiq - Minitest error: NoMethodError: unmocked method :==, expected one of [] #1291

Closed
PragTob opened this Issue Dec 2, 2013 · 3 comments

Projects

None yet

3 participants

@PragTob
PragTob commented Dec 2, 2013

Hi all,

there has been a bug report on the mailing list that specs fail on JRuby for the beloved sidekiq project for JRuby. Specs pass with Ruby 1.9.3 and 2.0. It seems like the bug manifests in the minitest library but I haven't done any kind of deep investigation.

The error message is this (as observed in this build and locally, jruby version 1.7.8):

manager#test_0002_assigns work to a processor:
NoMethodError: unmocked method :==, expected one of []
org/jruby/RubyArray.java:1957:in `=='
org/jruby/RubyHash.java:1069:in `=='
org/jruby/RubyArray.java:1303:in `include?'
org/jruby/RubyArray.java:1613:in `each'
org/jruby/RubyHash.java:1338:in `each'
/home/travis/build/mperham/sidekiq/test/test_manager.rb:24:in `test_0002_assigns work to a processor'
org/jruby/RubyArray.java:2413:in `map'
org/jruby/RubyArray.java:2413:in `map'
org/jruby/RubyArray.java:1613:in `each'

The error occurs during the following spec (no before hooks or something that I am aware of):

   it 'assigns work to a processor' do
      uow = Minitest::Mock.new
      processor = Minitest::Mock.new
      processor.expect(:async, processor, [])
      processor.expect(:process, nil, [uow])

      mgr = Sidekiq::Manager.new(options)
      mgr.ready << processor
      mgr.assign(uow)
      assert_equal 1, mgr.busy.size

      processor.verify # stack trace originates here
    end

JRuby is on the allowed failures for sidekiq, would be great to get it out of there,

Cheers,
Tobi

@petergoldstein

To follow on to Tobi's comments, the spec in question (and similar specs) pass on MRI. The reason for the failure appears to be that in JRuby '==' is called for objects in an Array, while the '==' method is not on the automatically permitted method list for Minitest::Mock objects.

Here's a simple spec, not Sidekiq specific, that fails with this exception:

it 'computes equality when using arrays containing Minitest::Mock objects' do
  item = Minitest::Mock.new
  item_array = [item]
  other_item_array = [item]
  assert_equal item_array, other_item_array
end

From examining the code it looks like MRI might be using '===' for comparing the individual elements in an array. I'm not sure if this is correct, but rb_equal is being called on the individual elements of the two arrays and the comments indicate that this method corresponds to '==='

@enebo
Member
enebo commented Dec 3, 2013

Ok I have a theory but I want to reduce this a bit more. I think you are correct that rb_equal is being called but I think it is being called directly in C and is not a Ruby dispatch. Since the test case is two identical objects in the array the first if statement in rb_equal will just return out without performing ==. In JRuby, we seem to always be calling == and we do not have this short-cut. I can easily get MRI to match our behavior:

gem 'minitest'
require 'minitest/mock'

item = Minitest::Mock.new
item2 = Minitest::Mock.new
[item] == [item2]

We will fix this and their optimization reduces dynamic dispatches but from a mocking perspective their behavior feels inconsistent. OTOH, I have not fully fixed this yet so my theory could be wrong.

@enebo
Member
enebo commented Dec 3, 2013

Fixed in commit a054e67. Need to get a test added somewhere for this.

@enebo enebo closed this Dec 3, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment