Unit testing after adding database with Hibernate - java

I've added database functionality using hibernate to a system which was in memory up to this point. When all the data was it the memory I was able to use JUnit which restored the original data after each test.
Is there a way to achieve the same result with the new hibernate addition?
By "the same result" I mean start with the database at its original state, do the test which can alter the database, and restore the database to its original state.
Up until now, my ideas are:
In memory database (which is a Hibernate feature) but that won't allow me to use my actual data.
Add "testing flag" to me DOA won't commit the changes if set.
I am sure there is a better solution, but I haven't found anything better yet.

You could start the database transaction before each test:
#PersistenceContext
private EntityManager em;
#Before
public void init() {
em.getTransaction().begin();
}
#After
public void destroy() {
em.getTransaction().rollback();
}
This way, each test has a transaction running before the test starts and this transaction is rolled back after the test finishes, so you always discard all changes the current test underwent.

I think we should be clear with the definition of Unit Test. Unit Test must only test a small unit (a public method) in the application.
Assuming you have a DAO layer which uses Hibernate to interact with Database. Now the Hibernate uses a SessionFactory that requires a dataSource. The data source of the Unit Test should not be same as the one for your production application.
The idea is to define a test datasource and use a in memory DB (hsqldb or any other). For each of the test case you can execute some queries on the in memory DB, using the test dataSource and clear that after the execution of the Unit Test. For each Unit Test you should execute the query so that the test data setup is done for that particular test.
For e.g.: If you want to test the following:
1) Create Account
2) Update Account
3) Delete Account
Then there are three test scenarios and there can bee multiple Unit Tests possible for each of the scenario.
Now before executing the Create Account Test, it is important that the DB doesn't have this account. and then you call the createAccount method in the DAO to test the same. No need to verify if the result is in DB or not. Just check the return of your method and if it is same as expected on a successful account creation then your test case should pass.
For Update Account, your setup method should insert one account through query and then you must call the updateAccount in DAO for this account id and so on.
Please stick to the definition of Unit Tests and do not use it for testing more than one functionality at a time.
Hope this helps.

Related

Reduce or group Database service calls into batch

I have some functional units of transactional work within service(s) which involved multiple calls to different DAOs (using jdbcTemplate or NamedJdbcTemplate) that return sequence generated IDs to run the next select/insert or run some void insertion in the order of calls. Each DAO call represents a sql statement which will require a call to the Database. For example
#Transactional
public void create() {
playerService.findByUserName(player.getPlayerId());
roleService.insertRoleForPlayer(player.getId(), ROLE_MANAGER);
leagueScoringService.createLeagueScoringForLeague(leagueScoring);
leagueService.insertPlayerLeague(player, code);
Standing playerStanding = new Standing();
playerStanding.setPlayerId(player.getId());
playerStanding.setPlayerUserName(player.getPlayerId());
playerStanding.setLeagueId(leagueId);
standingService.insertStandings(ImmutableList.of(playerStanding));
}
Preferably I want to batch up the calls or ensure they all get executed together on the DB. Note I do not use hibernate and I don't want to either. What is the best approach for this?

Why isn't my Hibernate insert reflected in my Hibernate query?

I've been asked to write some coded tests for a hibernate-based data access object.
I figure that I'd start with a trivial test: when I save a model, it should be in the collection returned by dao.getTheList(). The problem is, no matter what, when I call dao.getTheList(), it is always an empty collection.
The application code is already working in production, so let's assume that the problem is just with my test code.
#Test
#Transactional("myTransactionManager")
public void trivialTest() throws Exception {
...
// create the model to insert
...
session.save(model);
session.flush();
final Collection<Model> actual = dao.getTheList();
assertEquals(1, actual.size());
}
The test output is expected:<1> but was:<0>
So far, I've tried explicitly committing after the insert, and disabling the cache, but that hasn't worked.
I'm not looking to become a master of Hibernate, and I haven't been given enough time to read the entire documentation. Without really knowing where to start, this seemed like this might be a good question for the community.
What can I do to make sure that my Hibernate insert is flushed/committed/de-cached/or whatever it is, before the verification step of the test executes?
[edit] Some additional info on what I've tried. I tried manually committing the transaction between the insert and the call to dao.getTheList(), but I just get the error Could not roll back Hibernate transaction; nested exception is org.hibernate.TransactionException: Transaction not successfully started
#Test
#Transactional("myTransactionManager")
public void trivialTest() throws Exception {
...
// create the model to insert
...
final Transaction firstTransaction = session.beginTransaction();
session.save(model);
session.flush();
firstTransaction.commit();
final Transaction secondTransaction = session.beginTransaction();
final Collection<SystemConfiguration> actual = dao.getTheList();
secondTransaction.commit();
assertEquals(1, actual.size());
}
I've also tried breaking taking the #Transactional annotation off the test thread and annotating each of 2 helper methods, one for each Hibernate job. For that, though I get the error: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here.
[/edit]
I think the underlying DBMS might hide the change to other transactions as long as the changing transaction is not completed yet. Is getTheList running in an extra transaction? Are you using oracle or postgres?

Spring transaction whit several operations and rollback

I have to write some methods to change values into database and make some operations on file system.
So I have to make this sequence of step:
Set the boolean Updating field to true into database. It is used to avoid access to file system and database information that are linked with this value (for example a fleet of cars)
Make some operation on the database. For example change the date, name, value or other fields. These changes affect more database tables.
Make change to file system and database
Set the boolean Updating to false
As you can imagine I have to manage errors and start rollback procedure to restore database and file system.
I have some doubt about how I can write my method. I have:
The entity
The repository interface that extends JpaRepositoryand has Query creation from method names and #Query annotated with #Transactional if them write into database (otherwise I recevied error)
The service interface
The service implementation that contains all the method to make simple changes to database. This class is annotated with #Transactional
From the other classes I call service methods to use database but if I call some of these methods I write each value into database so it isn't possible to throw rollback, or I wrong?
The step 1 has to be write immediatly into database instead the other changes should be use #Transactional properties, but just adding #Transactional to my method is enough? For file system rollback I create a backup of all subfolders and restore them in case of error.
For example:
#Transactional(rollbackFor=FileSystemException.class)
private void changeDisplacement(int idApplication, int idDisplacement){
applicationServices.setUpdating(true); //this has be to write immediatly into database so that the other methods can stop using this application
Application application = applicationServices.getId(idApplication);
application.setDisplacement(displacementServices.getId(idDisplacement));
//OTHER OPERATIONS ON DIFFERENT TABLES
//OPERATIONS ON FILE SYSTEM CATCHING ALL EXCEPTION WITH TRY-CATCH AND IN THE CATCH RESTORE FILESYSTEM AND THROW FileSystemException to start database rollback
//In the finally clause use applicationServices.setUpdating(false)
}
Can it work with this logic or the #Transactional field is wrong here?
Thanks
#Transactional is OK here. The only thing is you need to set propagation of applicationServices.setUpdating to REQUIRES_NEW so that it gets committed individually:
public class ApplicationServices {
#Transactional(propagation=Propagation.REQUIRES_NEW)
public void setUpdating(boolean b) {
// update DB here
}
}
In the case of the exceptions, it will still update the DB as long as you have the call to setUpdating in the finally block.
There are multiple questions here and some of them are hard to grasp, here is a bit of input. When you have this:
#Transactional(rollbackFor=FileSystemException.class)
private void changeDisplacement(int idApplication, int idDisplacement){
applicationServices.setUpdating(true);
That flag will hit the database only when the #Transactional finishes. The change stays in hibernate context, until the end of #Transactionl method.
So while you execute changeDisplacement and someone else comes and reads that flag - it will see false (because you have not written it to the DB just yet). You could get it via READ_UNCOMMITTED, but it's up to your application if you allow this.
You could have a method with REQUIRES_NEW and set that flag to true there and in case of revert update that flag back.
Generally updating both the DB and file system is not easy (keeping them in sync). The way I have done it before (might be better options) is register events (once a correct DB was made) and then write to the filesystem.

Test DAO class which is using JDBC with Mockito

I am trying to make unit test on a DAO class using Mockito. I have written some unit test before but not on a DAO class using some data base(in this case JDBC and MySQl).
I decided to start with this simple method but I do not now which are the good practices and I do not know how to start.
I do not know if this is important in this case but the project is using Spring Framework.
public class UserProfilesDao extends JdbcDaoSupport {
#Autowired
private MessageSourceAccessor msa;
public long getUserId(long userId, int serviceId) {
String sql = msa.getMessage("sql.select.service_user_id");
Object[] params = new Object[] { userId, serviceId };
int[] types = new int[] { Types.INTEGER, Types.INTEGER };
return getJdbcTemplate().queryForLong(sql, params, types);
}
}
If you really like to test the DAO create an in memory database. Fill it with the expected values, execute the query within the DAO and check that the result is correct for the previous inserted values in the database.
Mocking the Connection, ResultSet, PreparedStatement is too heavy and the result are not as expected, because you are not accessing to a real db.
Note: to use this approach your in memory database should have the same dialect of your phisical database, so don't use specific functions or syntax of the final database, but try to follow the SQL standard.
If you use an in memory database you are "mocking" the whole database. So the result test is not a real Unit test, but is not also an integration test. Use a tool like DBUnit to easily configure and fill your database if you like this approach.
Consider that mocking the database classes (PreparedStatement, Statement, ResultSet, Connection) is a long process and you are not granted that it works as expected, because you are not testing the right format of your sql over an sql engine.
You can also take a look to an article of Lasse Koskela talking about unit testing daos.
To test the DAO you need to:
Empty the database (not necessary for in memory db)
Fill the database with data example (automatic with db unit, done in the #BeforeClass or #Before method)
Run the test (with JUnit)
If you like to formally separated real unit tests from integration tests you can move the DAO tests on a separate directory and test them when needed and in the integration tests.
A possible in memory database that has different compatibility modes is H2, with the following database compatibilities:
IBM DB2
Apache Derb
HSQLDB
MS SQL Server
MySQL
Oracle
PostgreSQL

Transactional Spring Junit4 test cases for Validation scenario

I am using Spring,JPA using hibernate for service -> dao layer.
Transactions are spring managed.
I have validation testcase wherein , I need to validate for duplicate data insertion and throw an exception.
In my testcase ,which is extension to AbstractTransactionalJUnit4SpringContextTests
I have configured #TransactionConfiguration with defaultRollback as true and bean name for transaction manager (in my case its a bean of JpaTransactionManager)
I execute this testcase as below steps
Create a record with call to dao.create(entity); (this will succeed)
Create a same record (with all the attributes same as set in step 1) and call dao.create(entity) (this must fail, but its not failing)
In my create(entity) method I make call to validate() method, which fires scalar object query (JPQL) to validate.
I expect validation to be failed, but this works without exception and duplicate data gets inserted in DB.
I tried debugging (enabled hibernate logs),I found that the select query (scalar query) fails to get the proper data (ideally it should fetch at least 1 record, as I inserted data for it in step 1 listed above.)
I see Insert query for step1 in logs ,before select query for validation.
Is there any other way to write test case for such scenario which involves spring / jpa with hibernate?
Please post your views
Thanks in advance!!
Can you please paste your test here, I believe each test is running in its own transaction, and you have defaultRollback as true. That should be the issue.

Categories