15

Is there some kind of JDBC driver which simply ignores database calls?

For the development I am migrating an application to a virtual machine. Here I want to work on the GUI part only. But the application makes several requests to a database which doesn't let the application even start. I don't want to change the application code at this time since the database is pretty much coupled.

So I was thinking there could be a JDBC driver which just returns empty results for queries.

Kai
  • 38,985
  • 14
  • 88
  • 103
  • A somewhat different approach I've seen was to stub the database with an in-memory hsqldb initialized from textfiles. – flup Aug 08 '13 at 19:08

7 Answers7

14

I decided to write an own simple mock driver. This was pretty much straight forward and did what I want. I can switch the database driver of the application by a configuration file so I could let the application use my driver on a simple way.

Then I extended the driver to return data which it parses from CSV files. I published the code on google code maybe someone else can get use of it: dummyjdbc

Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
Kai
  • 38,985
  • 14
  • 88
  • 103
10

There are some "void" JDBC drivers as part of Mocking framewroks, for example MockDriver from Mockrunner.

But using it requires some coding.

That's because when Java application connects to a database it provides a JDBC URL in form jdbc:mysql://localhost. The system is searching which driver is registered in it to handle this kind of URL and chooses the right driver. The info about which URL type driver supports is contained in the driver itself, and it's impossible for a mock driver to hold all known URL types in it - there's no such thing as wildcarding there and any list would not be full.

So, if you're able to call JDBCMockObjectFactory.registerMockDriver() in the application before it connects to the database - it will do the job. If not - I don't think it's possible. However, slight modification of the driver code would do it... but again - coding is required.

Oleg Mikheev
  • 17,186
  • 14
  • 73
  • 95
6

jOOQ ships with a MockConnection that can be provided with a MockDataProvider, which is much easier to implement than the complete JDBC API. This blog post shows how to use the MockConnection: http://blog.jooq.org/2013/02/20/easy-mocking-of-your-database/

An example:

MockDataProvider provider = new MockDataProvider() {

    // Your contract is to return execution results, given a context
    // object, which contains SQL statement(s), bind values, and some
    // other context values
    @Override
    public MockResult[] execute(MockExecuteContext context) 
    throws SQLException {

        // Use ordinary jOOQ API to create an org.jooq.Result object.
        // You can also use ordinary jOOQ API to load CSV files or
        // other formats, here!
        DSLContext create = DSL.using(...);
        Result<MyTableRecord> result = create.newResult(MY_TABLE);
        result.add(create.newRecord(MY_TABLE));

        // Now, return 1-many results, depending on whether this is
        // a batch/multi-result context
        return new MockResult[] {
            new MockResult(1, result)
        };
    }
};

// Put your provider into a MockConnection and use that connection
// in your application. In this case, with a jOOQ DSLContext:
Connection connection = new MockConnection(provider);
DSLContext create = DSL.using(connection, dialect);

// Done! just use regular jOOQ API. It will return the values
// that you've specified in your MockDataProvider
assertEquals(1, create.selectOne().fetch().size());

There is also the MockFileDatabase, which helps you matching dummy results with SQL strings by writing a text file like this:

# This is a sample test database for MockFileDatabase
# Its syntax is inspired from H2's test script files

# When this query is executed...
select 'A' from dual;
# ... then, return the following result
> A
> -
> A
@ rows: 1

# Just list all possible query / result combinations
select 'A', 'B' from dual;
> A B
> - -
> A B
@ rows: 1

select "TABLE1"."ID1", "TABLE1"."NAME1" from "TABLE1";
> ID1 NAME1
> --- -----
> 1   X
> 2   Y
@ rows: 2
Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
2

My framework Acolyte is a tested JDBC driver designed for such purposes (mock up, testing, ...): https://github.com/cchantep/acolyte

It already used in several open source projects, either in vanilla Java, or using its Scala DSL:

// Register prepared handler with expected ID 'my-unique-id'
acolyte.Driver.register("my-unique-id", handler);
// then ...
Connection con = DriverManager.getConnection(jdbcUrl);
// ... Connection |con| is managed through |handler|
cchantep
  • 9,118
  • 3
  • 30
  • 41
  • @Cities I know you mean well, but targeting a user like this and leaving so many messages is bordering on harassment. Please don't do this again. Flag and allow the moderators to handle it. –  Nov 28 '18 at 20:56
  • When you've edited your posts to expressly include your affiliation, flag for mods to undelete. –  Nov 28 '18 at 20:57
1

If you want to do unit tests, not an integration tests, than you can use a very basic and simple approach, using Mockito only, like this:

public class JDBCLowLevelTest {

    private TestedClass tested;
    private Connection connection;
    private static Driver driver;

    @BeforeClass
    public static void setUpClass() throws Exception {
        // (Optional) Print DriverManager logs to system out
        DriverManager.setLogWriter(new PrintWriter((System.out)));

        // (Optional) Sometimes you need to get rid of a driver (e.g JDBC-ODBC Bridge)
        Driver configuredDriver = DriverManager.getDriver("jdbc:odbc:url");

        System.out.println("De-registering the configured driver: " + configuredDriver);
        DriverManager.deregisterDriver(configuredDriver);

        // Register the mocked driver
        driver = mock(Driver.class);
        System.out.println("Registering the mock driver: " + driver);
        DriverManager.registerDriver(driver);
    }

    @AfterClass
    public static void tearDown() throws Exception {
        // Let's cleanup the global state
        System.out.println("De-registering the mock driver: " + driver);
        DriverManager.deregisterDriver(driver);
    }

    @Before
    public void setUp() throws Exception {
        // given
        tested = new TestedClass();

        connection = mock(Connection.class);

        given(driver.acceptsURL(anyString())).willReturn(true);
        given(driver.connect(anyString(), Matchers.<Properties>any()))
                .willReturn(connection);

    }
}

Than you can test various scenarios, like in any other Mockito test e.g.

@Test
public void shouldHandleDoubleException() throws Exception {
    // given
    SomeData someData = new SomeData();

    given(connection.prepareCall(anyString()))
            .willThrow(new SQLException("Prepare call"));
    willThrow(new SQLException("Close exception")).given(connection).close();

    // when
    SomeResponse response = testClass.someMethod(someData);

    // then
    assertThat(response, is(SOME_ERROR));
}
Paweł Prażak
  • 3,091
  • 1
  • 27
  • 42
1

Never heard of such a driver myself. If you don't find one, you could instead use a DB like HSQLDB. You can configure it to use in-memory tables, so nothing else gets written to disk. You would have to use a different connection string, though.

Peter
  • 531
  • 4
  • 4
0

If you're using Spring, make your own class that implements Datasource and have the methods do nothing.

Alex Beardsley
  • 20,988
  • 15
  • 52
  • 67