5

I'm trying to mock the Query class of JDBI with mockito, however it fails to mock the methods of its base class SqlStatement.

When running the code below the when statement is actually calling the concrete implementation in the base class and fails with NullPointerException.

import java.util.Map;

import org.junit.Test;
import org.skife.jdbi.v2.Query;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class TestClass {
    @Test
    public void testBind() {
        Query<Map<String,Object>> mockQuery = mock(Query.class);
        when(mockQuery.bind("xxx", 5)).thenReturn(mockQuery); //this line fails
        //more stuff here
    }
}

I've tried this with EasyMock as well and got the same results, it fails to mock this method.

More Info:

  • Mockito version is 1.9.5
  • JDBI version is 2.4.1 (the one that currently ships with dropwizard)

The exception is:

    java.lang.NullPointerException
        at org.skife.jdbi.v2.SQLStatement.bind(SQLStatement.java:434)
        at TestClass.testBind(TestClass.java:17)
            at ....

Any ideas how to work around this?

LiorH
  • 18,524
  • 17
  • 70
  • 98
  • This isn't an answer, but in my experience mocking this kind of DAO code is a waste of time. It won't expose errors you make in your use of the JDBI API. Write tests against a real database. – artbristol Jan 09 '13 at 19:35

2 Answers2

4

bind methods in SqlStatement are final (for exemple SQLStatement#bind(String, int)), so you cannot mock them using Mockito, it's a limitation of the JVM (EDIT:) that mockito cannot bypass at the moment.


EDIT2: Note that as the comments below point out, there is some misunderstanding of what's written above, and this requires clarification on my part:

Your options are to change your design so you won't have to stub those interactions, or you have to use PowerMock which uses complicate tricks with classloaders to rewrite class bytecode (not my preferred approach, though PowerMock is technically impressive).

Hope that helps.

bric3
  • 40,072
  • 9
  • 91
  • 111
  • 1
    It's a limitation of Mockito, not of the JVM, as implied by your own comment that PowerMock supports it. Not to mention that JMockit *also* supports it, even though it does not "use complicated tricks with classloaders". – Rogério Jan 10 '13 at 15:25
  • @Rogerio See the edit : "that mockito cannot bypass at the moment" – bric3 Jan 11 '13 at 09:10
  • 1
    I saw it. It's still not a limitation of the JVM, as even the official Mockito documentation [says](http://code.google.com/p/mockito/wiki/FAQ). (But I am repeating myself...) If there is a goal to "bypass it", I would suggest incorporating code from PowerMockito (ie, using a custom classloader). The only other choice would be to use JMockit's approach, with use of `java.lang.instrument`. – Rogério Jan 11 '13 at 10:38
  • 1
    @Rogerio Not being able to redefine a final IS a limitation of the JVM. When the JVM is reading the class bytecode it ensures that it is compliant with the the JLS. Try it you will have a VerifyError. Overcoming it is a trick that is OUTSIDE of the JVM, PowerMock uses a specific classloader that will reload classes and definalize them via ASM, JMockit uses another trick (an agent i believe) to be able to definalize them. – bric3 Jan 11 '13 at 10:59
  • 1
    Redefining a final class or method is exactly what you can do with the `java.lang.instrument.Instrumentation.redefineClass` method (which works for *any* class, `final` or not). This is a standard Java SE 5+ service, which exposes the "hot swap" functionality the JVM had since long before. This is not outside the JVM. It *is* the JVM. And JMockit does not "definalize" classes, it doesn't need to. – Rogério Jan 11 '13 at 13:29
  • 1
    Yeah well that's what I define as a trick outside the JVM, this API is for java agents because the Instrumentation implementation is only passed in a premain method. It is a clever idea that JMockit uses, but in my opinion it's OUTSIDE the JVM that the magic happen, meaning the JVM only offer tools like classloaders and agents to do various things. Bypassing or better overcoming the final method limitation is made by external libs like ASM, JMockit, etc. All this arguing for choice of word. – bric3 Jan 11 '13 at 14:49
  • 1
    An opinion doesn't change the fact that the entire instrumentation machinery is internal to the JVM, just like Reflection or classloading. JMockit never goes outside the running JVM instance from which it is used. My intent was not to argue over the exact choice of words, but to call attention to a) an apparent attempt to deny the known (and documented in the official FAQ) limitations of Mockito, and b) to denigrate other mocking tools which happen to not have the same limitations, by using phrases such as "complicated tricks". – Rogério Jan 11 '13 at 16:01
  • 1
    Well a) redefining final is a limitation of JVM, it's in the JLS, but OK it is it's a LIMITATION of Mockito as well because overcoming it is not implemented, b) my intention was never to denigrate these tool, these things are inherently intricate, CGLIB internals are complicate, and Mockito uses CGLIB. I never said that instrumentation is not in the JVM that was never the point of my comments, but that the way it is instrumented is not, ASM is not par of the JVM, and neither are the implementations of CLassTransformers used here and there in a agent! – bric3 Jan 11 '13 at 19:27
0

Try

Mockito.doReturn(mockQuery).when(mockQuery).bind("xxx",5);
bstick12
  • 1,699
  • 12
  • 18
  • Can you post the exception and the version of Mockito you are using. I've just run that both ways succesfully with Mockito 1.9.0 and jdbi 2.9.4 – bstick12 Jan 09 '13 at 17:46