How to use the actual database for integration testing? - java

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.

Related

Prevent tests from running with specific spring profile

I have some integration tests that create/delete entries in my database. The problem is if I run the tests with
spring.profiles.active=prod
my production database gets deleted (as the tests clear the database). Is there any way to prevent tests from running on this specific spring profile?
I have seen this thread: How to prevent running tests when specific spring profile is active? but there was no useful answer.
Thank you
There could be multiple solution to your problem.
Using in-memory databases like H2 for Sql and flapdoodle for no-sql for running tests. **preferred way
Create a separate properties file with clone of spring properties. Just change the database properties/spring profile or other things. Use this properties file with #testpropertysource on test class.
Use #dirtiescontext on tests to create/delete impacted rows only.
Another thing you can do is create stud classes your database layer to mock operations.
I was able to resolve the problem following this comment: https://stackoverflow.com/a/32892291/8679100. The solution is not perfect, as I need to verify if the prod profile is active in #BeforeAll and #AfterAll, but it does work. Furthermore, System.getProperty("spring.profiles.active", "");didn't actually work, butArrays.stream(environment.getActiveProfiles()).anyMatch(env -> (env.equalsIgnoreCase("prod")))``` did
You can use #IfProfileValue annotation to disable the test. it's not directly depend on the spring profile, but you can easily use config file to set the value you want based on the spring profile.
Having said that - it sounds very risky to run tests that delete entries from the db (or any other db transaction) on production DB. I support what #Sachin suggested above - run your test on tests environment, not production

Is it possible to rollback the database after each JUnit test case without using Spring framework?

I'm trying to test the service which updates multiple tables from the database and I want to rollback the database to previous state after each test case. All solutions I have found are using #Transactional and #Rollback from Spring framework, but since my application is not a Spring web application, I would like to use javax #Transactional, which does not work for me.
Is this possible with javax at all or anything else except the Spring?
Rollback a transaction isn't a good idea for test (integration test) as the constraint may not be validated before the commit.
You should:
have a DB only for integration tests (or an embedded db or a container db or in RAM db)
execute, for example in a class rule or in a test rule, script SQL in order to bring the db in a known status
execute a test
if test modifies the db then run a truncate of tables modified (again or in your class or test rule) and, before peform a new test, run again the script at point 2
run integration tests not so often as unit tests
Better idea is to use in-memory database:
H2 https://www.h2database.com/
Recommendation for a Java in memory database
Not always and not everything in database can be rolled-back to initial state ( ex. sequences ).

Unit test case execution with local Impala connection

I'm have an application which fetches data from Impala, does the process and generates final reports. written some test cases to valiate the code in my local. During the test case execution it connecting to remore impala server and fetching the data.
Due to the remote jdbc conection to impala it is takeing long time to complete all my test cases.
I thought if impal can we setuped in local as jar or somhing I will help in reducing the test case exectuion time.
Can some one suggest something to achive my objective.
Thinking somehting liek h2 datases which supports all impala queries so I update laod data in execute
I tryied H2 database, postgress, mysql etc
i highly suggest you to mock those impala calls. you can have a look at the mockito framework (https://site.mockito.org/) which offers ways of simple method mocking.
using this, you can just define in your test what the result in your impala query is and use this mocked data for your actual test.

How can I set a separate Database for testing?

Actually when running Selenium test case. I takes the live (Main Database) database which is configured in Glassfish. If we add records for testing purpose then it will replicate in the Main database. This is not good know. So is there any way to have separate DataBase for Selenium test case.
For Glassfish:
Define your JDBC Connection Pool resource to refer to different databases for your production server vs your development server.
If you're not running two different servers, then your first step is to fix that, and be running different servers. And different database servers. Never point your development machine/server at production data, or even the production database server.
If you are trying to do a unit test that should run on deployment, then you must create a second connection in your unit test program.
You might want to use an in memory database for this, maybe HSQL which comes bundled with the java sdk so you don't need to install any drivers and also you won't need to cleanup anything after the tests are run.
How you create the second connection depends on what you are using, hopefully you will have a central class or method to get the database connections, something like this:
Connection c = MyConnectionClass.getConnection();
so you will have it easy modifying the getConnection method so you can point out to the HSQL direct connection for your tests, with something like this:
public Connection getConnection(){
if(testing){
Connection c = DriverManager.getConnection("jdbc:hsqldb:mem:mymemdb", "SA", "");
}else{
//get your connection from your pool or whatever you are doing right now
}
}
or also you can be more correct and create a mock for this class. But I'm not sure if selenium supports this out of he box.
And of course, you will need to create your database schema into the in memory database before beginning the tests. If you use hibernate or JPA for example then that should be simple (be sure that you add a second persistence unit and use that in this case), if not then you should have the scripts for your database and run them with JDBC as you would run them in any database.
Also if you don't want to run the scripts every time the tests are run use hsql but in file mode (change the jdbc:hsqldb:mem for jdbc:hsqldb:file it will use a file to store the database).
here's some info about the hsql database if you want to know more: link
Also if you dont like hsql you can try sqlite (only for file mode) which I think has more tools for accessing it

How to generically test a database connection with hibernate

I have a service method on an api that can be called to check the health of my database connection.
The method is pulling the query string from a properties file (depends on DB vendor, using Sybase and HSQL for now, more in future), and executing it. Then the method lets the caller know if it succeeded or failed.
In addition to this, I was using the Query.setHint("javax.persistence.query.timeout") to set a timeout on the query:
javax.persistence.EntityManager entityManager;
...
Query heartbeatQuery = entityManager.createNativeQuery(heartbeatQueryString);
heartbeatQuery.setHint("javax.persistence.query.timeout", heartbeatTimeout);
heartbeatQuery.getResultList();
My problem is the timeout property is working against my Sybase DB, but not against my HSQL DB. It sounds like it depends on the vendor, so I don't know for sure when it will work.
Is there a better way to generically test the DB connection & include some kind of timeout parameter?
Well sadly no. JPA's query hints are not mandatory, i.e. it's up to the implementator (EclipseLink, Hibernate, etc) to enforce them or not. Moreover, even if the implementator does chose to recognize a certain query hint, if that hint's functionality is not supported by the database then it won't work (here some implementators are nice and tell you if a certain hint won't work agains the current db while others fail silently). In the case of HSQLDB there's no way to set the query timeout. You can only set a timeout for the login (i.e. how long should it wait for a successful login before failing), but not for the queries duration.
Things are not so grim however. On the one hand, even if you'd solve this, you'd still stumble over other issues with HSQLDB, as it does not support a lot of other nice functionalities that most dbs have. You should only use HSQLDB for basic integration/unit testing. For more involved testing, you can use the integrated MySQL Java library. You can find it here:
http://dev.mysql.com/doc/refman/5.0/en/connector-mxj.html
This is simply a packaged fully working Mysql server, which has a Java api for star and stop, works on most major OSs (win,lin, os x, etc). This way you can have your integration tests start a real Mysql server, and try your code there, where such stuff as a query timeout hint will work fine.

Categories