0

I'm writing a simplistic wrapper for JDBC in JRuby. Essentially I want to have a wrapper class DBW that takes a connection string and an optional initialization block.

require 'java'

class DBW
    def initialize (connection_string, &optional_init_block)
        if optional_init_block
            yield
        end

        @connection_string = connection_string
    end
    def get_connection
        Java::java.sql.DriverManager.getConnection(@connection_string)
    end
end

However in my test I want to use a test double as the Driver that registers with the DriverManager, so I've done the following in a RSpec test:

it "can produce a connection using the connection string" do 

    mock_conn = instance_double("Connection") # A dummy connection instance
    mock_driver = instance_double("Driver")
    allow(mock_driver).to receive(:acceptsURL).with(any_args) {
        # this doesn't get called when the wrapper calls getConnection on the DriverManager 
        true
    }
    # Expecting that connect would be called on the driver with the connecion string...
    allow(mock_driver).to receive(:connect).with(any_args).and_return(mock_conn)

    wrapper = DBW.new "jdbc:subprotocol:subname" do 
        # Initialize the DriverManager with the mock driver
        Java::java.sql.DriverManager.registerDriver(mock_driver)
    end

    # This should call in to DriverManager.getConnection
    conn = wrapper.get_connection
    expect(conn).to eq(mock_conn)

end

I get the following error running the test:

Failures:

  1) DBW can produce a connection using the connection string
     Failure/Error: Unable to find matching line from backtrace
     Java::JavaSql::SQLException:
       No suitable driver found for jdbc:subprotocol:subname
     # java.sql.DriverManager.getConnection(java/sql/DriverManager.java:689)
     # java.sql.DriverManager.getConnection(java/sql/DriverManager.java:270)
     # java.lang.reflect.Method.invoke(java/lang/reflect/Method.java:497)
     # RUBY.get_connection(/playground/dbw.rb:12)
     # RUBY.(root)(/playground/specs/dbw_spec.rb:39)

As noted in the test code the acceptsURL method is not getting called by the DriverManager. Any ideas what I'm missing?

Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121
Emil L
  • 20,219
  • 3
  • 44
  • 65

1 Answers1

1

you're very likely hit java.sql.DriverManager internals that seem to mess with you. if you look at the .java source you'll see there's checks when obtaining connection on the caller's loader (before calling connect - acceptsURL is not relevant here). that might be something that blows up within JRuby, you can verify a java.sql.Driver generated instance is there but not returned when using the API :

java.sql.DriverManager.class_eval do
  field_reader :registeredDrivers
end

# ...

Java::java.sql.DriverManager.registerDriver(mock_driver)

puts java.sql.DriverManager.registeredDrivers.to_s # returns the mock_driver (last)
# [driver[className=sun.jdbc.odbc.JdbcOdbcDriver@55974e79], driver[className=org.jruby.gen.InterfaceImpl1708146542@b02cbee]]
puts java.sql.DriverManager.getDrivers.to_a.inspect  # does not return the mock_driver
# [#<Java::SunJdbcOdbc::JdbcOdbcDriver:0x55974e79>]

I recommend you refactor your code to be not tight to DriverManager too much ... or simply try out with a "real" (Java) mock driver compiled into a .jar added to the class-path

kares
  • 7,076
  • 1
  • 28
  • 38