I am doing a software project, working with database and Java code and in the unit tests, we have to test the selects....I mean, the expectable is that the table that results from the selects, have to correspond with the output in the intellij.....
in a few words, how do I compare a table originated from SQL with output(system.out.println())
You can use an embedded database for that (for example H2).
The workflow would be as follows:
Define a schema for your tables and any initial values.
Start the embedded database at the beginning of your tests and populate it with the defined schema.
Connect to the embedded database from your test code, as you would connect to any database.
Run your SQL against the embedded database.
Do asserts on the results fetched from the database.
Stop the embedded database.
You can find a H2 tutorial here: https://www.tutorialspoint.com/h2_database/h2_database_jdbc_connection.htm
Related
I am developing Java Vert.x 3 application. I use HSQLDB for testing with in-memory DB and MySQL 8.0.20 for runtime. When the vertx verticle is deployed, it initializes the db and tables. Since this is a common code and there are differing SQL syntax between HSQLDB and MySQL and more ridiculously, the HSQLDB capitalizes all the property names and I have to double-quote the properties to use lower-case. I wonder how to achieve this. Here are my questions:
(1) HSQLDB uses "IDENTITY" keyword for creating the in-memory database table. This results in runtime error in MySQL DB as "IDENTITY" is not valid keyword. This poses a challenge that I am facing now.
(2) If it is not possible to have a common SQL syntax which satisfies both MySQL and HSQLDB, what's the best approach to split this common execution path based on the java application runtime profile since this DB initialization is done in the start function of the verticle which is the core of the application?
Any advice and insight is appreciated.
You can create the HSQLDB database with MySQL compatibility mode (append ;sql.syntax_mys=true to the JDBC URL. In this mode, you can do the following:
Use MySQL syntax for CREATE TABLE. HSQLDB understands MySQL's AUTO_INCREMENT keyword as an alias for IDENTITY. It also understands all other MySQL-specific syntax.
The capitalization of column names is really not a problem. With the column names capitalized, you can use any case without quoting in your SELECT statements. This means you can run the same query that you use for MySQL on HSQLDB.
See http://hsqldb.org/doc/2.0/guide/compatibility-chapt.html#coc_compatibility_mysql
Solution: Ditch HSQLDB and use H2 with database_to_upper=false option.
In production, we use SQL scripts (Postgres) to generate / update the schema. In my unit tests, I use configProperties.put("hibernate.hbm2ddl.auto", "create-drop"); in order to create the schema for my H2 database.
In order to isolate the unit tests better, I want to drop and recreate the database in between tests. I can wipe the H2 database with entityManager.createNativeQuery("DROP ALL OBJECTS").executeUpdate();, but how do I then get hibernate to re-create the schema?
Or is there an easy way to simply shut down H2 and create a new instance? I can create multiple instances by using "jdbc:h2:mem:"+UUID.randomUUID()+";MODE=DB2;" as my connection string, but I'm not sure how to shut down / destroy / clean up previous instances.
There were some proposed solutions to the question "How to test SQL statements in an application" -
Using RAM memory - I can't change the configuration of staging environment where testing happens.
Using H2 - Not very compatible even in PostgreSQL mode
Use the same database to run the tests.
Using in-memory mode - PostgreSQL doesn't have one.
The third one was viable and I looked into Test Containers which is actually a beautiful solution but a relatively new one. As a result, our company is sceptical of adopting it.
We use Mybatis to access PostgreSQL.
Another way would be to recreate entire schema and populate required tables before tests. Here is the problem, I could create and delete schema with tables with the same name. To avoid name collision I'd have to change schema's name, as a result, even queries should be renamed which is not at all preferred. Is there a way to do this without changing queries but pointing them to the dummy schema.
You should NOT change your queries. In tests you should only change the connection url your application will use. The problem is, how to get that url working.
To have full test coverage you need the same db (as you noticed, h2 and other in-memory db are not very compatible). postgres doesn't have in-memory mode so you have to manage the lifecycle yourself. there is a few decisions you have to make. some of them:
where will you get the db from: require all the devs to provide postgres (installation / docker / vagrant) or automate the setup?
how to prepare db for tests: manual schema setup and cleanup?
how to reset db between tests: restart? always rollback? predefined and separately defined content? some kind of reverse operations?
if and how to make those tests fast?
there are some tools that can help you solve some of the problems:
testcontainers will help you provide
db.
dbunit - will help you prepare data for your test.
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
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
you can also fill the gaps on your own. as always it's a trade: time vs money
you can define database configuration for test purpose and connect to your real database base for execute tests. you should access to test database configuration in test classes.
for example, if you use spring and hibernate to connect to the database, you can define a test hibernate configuration xml file where it connect to test database. then in your test classes, use this configuration file as follow:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguratiion({testHibernate.xml, testSpring.xml , .... })
#TestExecutionListeners({...})
public class TestClass {
....
#Test
public void test1(){
...
}
}
so, you can access your test hibernate session factory to execute your queries.
In H2, it is easy to setup in-mem db for unit testing via RUNSCRIPT command as part of connection url itself (No hibernate & Spring required) to set it up.
h2 sample
jdbc:h2:mem:sample;INIT=RUNSCRIPT FROM 'classpath:scripts/create.sql'\\;RUNSCRIPT FROM 'classpath:scripts/create_2.sql'
I am trying to understand if there is a way to have a similar setup for hsqldb too? No success till now. If it is in documentation, point me to the specific link please.
Constraints:
HSQL should be in-memory only.
No Spring and Hibernate should be used.
Thanks
HSQLDB supports a memory database that is read from file, with no change automatically written to file.
Create the memory database with tables and required initial data, then save it with the SRCIPT 'filename' command.
Then open it as a file readonly database:
jdbc:hsqldb:file:filename;files_readonly=true
I'm currently writing some test cases using testng. I have a requirement to repeat same test set in H2 embedded db and in H2 in-memory db. What i want is to copy all tables and data from H2 embedded db to H2 in-memory db upon transition from one db type to another.
Is it possible ? thanks in advance.
you could use the script tool from here
http://h2database.com/javadoc/org/h2/tools/Script.html
to export the tables and data and then use runscript as descibed here
http://h2database.com/html/grammar.html#runscript
to import it in the other db