JDBI fluent api is unmockable

1,322 views
Skip to first unread message

djlig...@djlightsout.net

unread,
Jul 7, 2015, 12:51:04 AM7/7/15
to jd...@googlegroups.com
I had fallen in love with JDBI until I hit a snag trying to unit test it's fluent API. Since the bind methods are final, you cannot mock and verify invocations of the queries. Why are the bind methods final? Someone else posted a similar question regarding this awhile ago:

I don't want to have to wrap jbdi to accomplish simple mocking. 

Steven Schlansker

unread,
Jul 7, 2015, 12:41:59 PM7/7/15
to jd...@googlegroups.com
Personally, I think mocking JDBI leads to inherently extremely brittle tests. Have you considered instead wiring JDBI up to an in-memory database like H2? That way you can actually test your SQL code, and if you modify it to be functionally identical you do not break all of your brittle mocks.

Brian McCallister

unread,
Jul 8, 2015, 1:10:46 PM7/8/15
to jd...@googlegroups.com
You are right, JDBI is not really mock-friendly. If you are using
mocks heavily, you are probably better off using the sql object api,
or creating some kind of mockable DAO.

Aside from actually mocking JDBI, I have personally almost always
found mocking out low level database interaction usually hides bugs
rather than helps reliable code. This lead to me not bothering to make
JDBI play well with mocks.

-Brian
> --
> You received this message because you are subscribed to the Google Groups "jDBI" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to jdbi+uns...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

djlig...@djlightsout.net

unread,
Jul 9, 2015, 10:16:53 PM7/9/15
to jd...@googlegroups.com
I understand, even tho jdbi's is unmocksble in that sense, I was able to inject a instance of dbi with a mock connection pointing to an in memory database that mirrored my prod database. It ended up working out pretty well. Thanks for the feedback gents and keep up the good work Mr. McCallister!

Brian McCallister

unread,
Jul 9, 2015, 11:46:05 PM7/9/15
to jd...@googlegroups.com
Thank you, but Mr. Schlansker does more work than me lately :-D

-Brian

On Thu, Jul 9, 2015 at 7:16 PM, <djlig...@djlightsout.net> wrote:
> I understand, even tho jdbi's is unmocksble in that sense, I was able to inject a instance of dbi with a mock connection pointing to an in memory database that mirrored my prod database. It ended up working out pretty well. Thanks for the feedback gents and keep up the good work Mr. McCallister!
>

Stu S

unread,
Jun 10, 2020, 1:16:09 PM6/10/20
to jDBI

I know this is an old thread, but for anyone else wondering about this, it's become a more common issue to see mocking difficulties popping up, particularly with the spread of Kotlin (which defaults to final).

For the TLDR for the solution to this problem, here's an example from Javalin:

https://javalin.io/tutorials/mockito-testing

If you use a Kotlin unit testing framework, it will typically support mocking final methods.

Also, just to lend some perspective: I used to feel the same way about "unit" tests - why not connect to a in-memory/test/QA/staging DB!??

But I have since come around:
  1) First, terminology. If you're pulling dependencies into the component your testing, you're not unit testing, you're integration testing. Integration testing is invaluable, and catches many bugs unit tests cannot. This is true. Also, as long as we're going over terminology, yes, I'm conflating mocks & stubs, but for this conversation, that should be OK.

  2) As I moved around in my career, I found myself writing more and more complicated application-level logic (business logic). Unit tests are invaluable for isolating & verifying complicated application-level logic, particularly as let you built up confidence that a certain subset of code behaves a certain way.

  3) As an addendum to (2), unit tests are particularly valuable when making last-second updates to complicated business logic, that has existed for a long time, with many authors, some of who have not been around for a while.

It's true, that, at the level of "my system interacting with the DB", unit tests aren't particularly useful.

But at the level of "my complicated application logic, as required by product managers, and sometimes by *law*" is following the correct logic, canonical, isolated, unit tests are very useful. Particularly when it's not just the DB is a source of data for the logic - it's often the DB, and several other services, all of which depend on their own DBs, etc. And you don't necessarily want to re-design the whole code, possibly breaking that logic, without a good set of tests for that code. So "Re-write all that code so you can easily mock" isn't really a great option, sometimes.

You do *also* need integration level testing, because mocks are, in fact, generally useless verifying your queries, connection pooling, etc are all set up correctly. But in non-trivial applications, there is often a lot of code that lives outside of the code that is retrieving data from the DB. And sometimes it's nice to figure out what's wrong with that code, separate from the DB.

Just wanted to add a view from the other side of things.



On Thursday, July 9, 2015 at 8:46:05 PM UTC-7, Brian McCallister wrote:
Thank you, but Mr. Schlansker does more work than me lately :-D

-Brian

On Thu, Jul 9, 2015 at 7:16 PM,  <djlig...@djlightsout.net> wrote:
> I understand, even tho jdbi's is unmocksble in that sense, I was able to inject a instance of dbi with a mock connection pointing to an in memory database that mirrored my prod database. It ended up working out pretty well. Thanks for the feedback gents and keep up the good work Mr. McCallister!
>
> --
> You received this message because you are subscribed to the Google Groups "jDBI" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to jd...@googlegroups.com.

Steven Schlansker

unread,
Jun 10, 2020, 1:54:59 PM6/10/20
to jd...@googlegroups.com
Totally agree on your points re: testing units vs integration.
But I do have a somewhat strong personal opinion: it's better to write a DAO interface and replace that totally than it is to try to e.g. mock jdbi or jdbc.

Your test mocks become much simpler.  "When you dao.findByType(X), return List.of(item1, item2)", as opposed to, "when a particular SQL is bound and we capture binding argument at position 2 and ensure that it is a TypeArgument with value X, return a mocked resultset that has a single row prepared in its row buffer that knows how to implement JDBC getters".  In a way, your interface definition becomes the language that you're writing your mocks against.  Jdbi is not designed at all to be that interface.

It also opens additional possibilities.  For example for writing unit tests, we provide alongside the Jdbi DAO implementation a "Memory" implementation that replicates the essential structures of the database but with e.g. HashMap and List in memory.  This means that when you are writing application logic, you can use either the Memory implementation for quick unit testing, or the Jdbi implementation for integration testing or production runtime.  When you're writing DAO tests, make them run once against the real implementation and once agains the memory implementation.  This is very similar to mocking except instead of crafting the right mocks per-test you write a more complete implementation once and reuse it.

Once you get two high-quality implementations, you can write lightning-quick application tests that execute against a "pretty close to real but zero overhead" "DB" :)

That's not to say I'm mock-hostile.  If Jdbi can be more easily mocked by a small PR that drops a couple `final` modifiers, and people really want to do that, that seems OK to me.  I just am more and more coming around to the opinion that there's almost always a better way.

To unsubscribe from this group and stop receiving emails from it, send an email to jdbi+uns...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jdbi/24eee594-4540-4954-81a6-2c12be6d1558o%40googlegroups.com.

Stu S

unread,
Jun 10, 2020, 2:20:59 PM6/10/20
to jDBI

>But I do have a somewhat strong personal opinion: it's better to write a DAO interface and replace that totally than it is to try to e.g. mock jdbi or jdbc.

Ah ok. And, yes, unless it's a quick one-off program, DAO's are generally nicer to maintain, for a variety of reasons. But sometimes you want tests on existing code before you start changing (breaking) it ;)


> That's not to say I'm mock-hostile.  If Jdbi can be more easily mocked by a small PR that drops a couple `final` modifiers, and people really want to do that, that seems OK to me.  I just am more and more coming around to the opinion that there's almost always a better way.

Good to know! Balls in my court, then, I suppose, to get that PR, and somehow justify it :D.

I did get lazy (efficient?) and just use the mock-maker-inline to mock the final. It's easy once you know how, but just wanted to feel out the general opinion on mocks/stubs.

Thanks for the quick reply!

Best,
  -stu
To unsubscribe from this group and stop receiving emails from it, send an email to jd...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages