Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to simulate a DB for testing (Java)?

I'm programming in Java and my applications are making a lot of use of DB. Hence, it is important for me to be able to test my DB usage easily.
What DB tests are all about? For me, they should supply two simple requirements:

  1. Verify SQL syntax.
  2. More importantly, check that the data is selected/updated/inserted correctly, according to a given situation.

Well then, it seems that all I need is a DB.
But actually, I prefer not, as there are few difficulties using a DB for a test:

  • "Just get yourself a testing DB, how hard could it be?" - Well, in my working place, to have a personal testing DB is pretty impossible. You have to use a "public" DB, which is accessible for everyone.
  • "These tests sure ain't fast..." - DB tests tend to be slower than usual tests. It's really not ideal to have slow tests.
  • "This program should handle any case!" - It becomes somewhat annoying and even impossible to try and simulate each and every case in a DB. For each case a certain amount of insert/update queries should be made, which is annoying and takes time.
  • "Wait a second, how do you know there are 542 rows in that table?" - One of the main principles in testing, is to be able to test the functionality in a way different from that of your tested-code. When using a DB, there's usually one way to do something, therefore the test is exactly the same as the core-code.

So, you can figure out I don't like DBs when it comes to tests (of course I will have to get to this in some point, but I'd rather get there later on my testing, after I found most bugs using the rest of the test methods). But what am I looking for?

I'm looking for a way to simulate a DB, a mock DB, using the file system or just virtual memory. I thought that maybe there's a Java tool/package which allows to simply construct (using code interface) a DB mock per test, with simulated tables and rows, with SQL verification, and with a code interface for monitoring its status (rather then using SQL).

Are you familiar with this kind of tool?


Edit: Thanks for the answers! Although I was asking for a tool, you also provided me with some tips concerning the problem :) It will take me some time to check out your offers, so I can't say right now whether your answers were satisfying not.

Anyway, here's a better view of what I'm looking for - Imagine a class named DBMonitor, that one of its features is finding the number of rows in a table. Here is an imaginary code of how I would like to test that feature using JUnit:

public class TestDBMonitor extends TestCase {      @Override     public void setUp() throws Exception {         MockConnection connection = new MockConnection();         this.tableName = "table1";        MockTable table = new MockTable(tableName);         String columnName = "column1";        ColumnType columnType = ColumnType.NUMBER;        int columnSize = 50;        MockColumn column = new MockColumn(columnName, columnType, columnSize);        table.addColumn(column);         for (int i = 0; i < 20; i++) {            HashMap<MockColumn, Object> fields = new HashMap<MockColumn, Object>();            fields.put(column, i);            table.addRow(fields);        }         this.connection = connection;     }      @Test     public void testGatherStatistics() throws Exception {         DBMonitor monitor = new DBMonitor(connection);        monitor.gatherStatistics();        assertEquals(((MockConnection) connection).getNumberOfRows(tableName),                     monitor.getNumberOfRows(tableName));     }      String tableName;     Connection connection; } 

I hope this code is clear enough to understand my idea (excuse me for syntax errors, I was typing manually without my dear Eclipse :P).

By the way, I use ORM partially, and my raw SQL queries are quite simple and shouldn't differ from one platform to another.

like image 622
Eyal Roth Avatar asked May 30 '09 01:05

Eyal Roth


People also ask

How do you create a test DB?

In the ClearQuest Schema Repository Explorer view, click on the schema repository where you want to add the test database. Click View > ClearQuest Database Admin. The ClearQuest Database Admin view opens. In the ClearQuest Database Admin view, click the Create Database button.


2 Answers

Java comes with Java DB.

That said, I would advise against using a different type of DB than what you use in production unless you go through an ORM layer. Otherwise, your SQL might not be as cross-platform as you think.

Also check out DbUnit

like image 113
ykaganovich Avatar answered Sep 16 '22 17:09

ykaganovich


new answer to old question (but things have moved forward a bit):

How to simulate a DB for testing (Java)?

you don't simulate it. you mock your repositiories and you don't test them or you use the same db in your tests and you test your sqls. All the in-memory dbs are not fully compatible so they won't give you full coverage and reliability. and never ever try to mock/simulate the deep db objects like connection, result set etc. it gives you no value at all and is a nightmare to develop and maintain

to have a personal testing DB is pretty impossible. You have to use a "public" DB, which is accessible for everyone

unfortunately a lot of companies still use that model but now we have docker and there are images for almost every db. commercial products have some limitations (like up to a few gb of data) that are non-important for tests. also you need your schema and structure to be created on this local db

"These tests sure ain't fast..." - DB tests tend to be slower than usual tests. It's really not ideal to have slow tests.

yes, db tests are slower but they are not that slow. I did some simple measurements and a typical test took 5-50ms. what takes time is the application startup. there are plenty of ways to speed this up:

  • first DI frameworks (like spring) offers a way run only some part of your application. if you write your application with a good separation of db and non-db related logic, then in your test you can start only the db part
  • each db have plenty of tuning options that makes it less durable and much faster. that's perfect for testing. postgres example
  • you can also put the entire db into tmpfs

  • another helpful strategy is to have groups of tests and keep db tests turned off by default (if they really slows your build). this way if someone is actually working on db, he needs to pass additional flag in the cmd line or use IDE (testng groups and custom test selectors are perfect for this)

For each case a certain amount of insert/update queries should be made, which is annoying and takes time

'takes time' part was discussed above. is it annoying? I've seen two ways:

  • prepare one dataset for your all test cases. then you have to maintain it and reason about it. usually it's separated from code. it has kilobytes or megabytes. it's to big to see on one screen, to comprehend and to reason about. it introduces coupling between tests. because when you need more rows for test A, your count(*) in test B fails. it only grows because even when you delete some tests, you don't know which rows were used only by this one test
  • each tests prepares its data. this way each test is completely independent, readable and easy to reason about. is it annoying? imo, not at all! it let you write new tests very quickly and saves you a lot of work in future

how do you know there are 542 rows in that table?" - One of the main principles in testing, is to be able to test the functionality in a way different from that of your tested-code

uhm... not really. the main principle is to check if your software generates desired output in response to specific input. so if you call dao.insert 542 times and then your dao.count returns 542, it means your software works as specified. if you want, you can call commit/drop cache in between. Of course, sometimes you want to test your implementation instead of the contract and then you check if your dao changed the state of the db. but you always test sql A using sql B (insert vs select, sequence next_val vs returned value etc). yes, you'll always have the problem 'who will test my tests', and the answer is: no one, so keep them simple!

other tools that may help you:

  1. testcontainers will help you provide real db.

  2. dbunit - will help you clean the data between tests

    cons:

    • a lot of work is required to create and maintain schema and data. especially when your project is in a intensive development stage.
    • it's another abstraction layer so if suddenly you want to use some db feature that is unsupported by this tool, it may be difficult to test it
  3. testegration - intents to provide you full, ready to use and extensible lifecycle (disclosure: i'm a creator).

    cons:

    • free only for small projects
    • very young project
  4. flyway or liquibase - db migration tools. they help you easily create schema and all the structures on your local db for tests.

like image 43
piotrek Avatar answered Sep 20 '22 17:09

piotrek