Mock database driver

2020-04-02 08:04发布

问题:

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.

回答1:

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



回答2:

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.



回答3:

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


回答4:

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|


回答5:

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.



回答6:

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));
}


回答7:

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