Spring Hibernate Repository Testing with jUnit
Your tests do not automatically reset the persisted data. There are a couple of alternatives for solving or working around that:
1. Add the @Transactional
annotation to the tests.
Transactional tests are run in a transaction that gets rolled back after the test finishes, clearing any state persisted by the test. However, adding @Transactional
might hide some errors. See this article about it.
2. Create an @After
method that cleans any persisted changes
This might be cumbersome to maintain since you need to keep track of everything that the tests persist. One slightly more complicated but maintainable way to do this is to dump the DB before running the tests and then restore it from that dump after every test.
3. Implement the tests in a way that they don't break even if the DB already contains something.
Example:
@Test
public void testEntityGetsPersisted() {
int countBefore = getCurrentCountOfEntities();
persistNewEntity();
int countAfter = getCurrentCountOfEntities();
assertTrue(countAfter == countBefore + 1);
}
Amit
Updated on June 04, 2022Comments
-
Amit almost 2 years
First of all I am a newcomer for Spring + Hibernate development. Followed lots of tutorials books and I have created a sample application mainly on Spring, Hibernate based on the standards, I have started writing some test cases for Repository (DAO) methods ie find, findAll, save, delete.
Its really crazy when i execute the test class not all of the test cases execute properly for ex. particularly find & findAll methods. But when run them individually they pass perfectly.
Entity
@Entity @Table(name = "client_master") public class ClientMaster { private Long id; private Long version; private Date dateCreated; private Date lastUpdated; private String clientName; private List<ProjectMaster> projectMaster; @Id @GeneratedValue public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Version @Column(name = "version") public Long getVersion() { return version; } public void setVersion(Long version) { this.version = version; } @Column(name = "client_name", nullable= false,unique= true) public String getClientName() { return clientName; } public void setClientName(String clientName) { this.clientName = clientName; } @OneToMany(targetEntity = ProjectMaster.class, mappedBy = "lientMaster", cascade = CascadeType.ALL, fetch = FetchType.LAZY) public List<ProjectMaster> getProjectMaster() { return projectMaster; } public void setProjectMaster(List<ProjectMaster> ProjectMaster) { this.projectMaster = projectMaster; } @Column(name = "date_created" , nullable= false) @Temporal(TemporalType.TIMESTAMP) public Date getDateCreated() { return dateCreated; } public void setDateCreated(Date dateCreated) { this.dateCreated = dateCreated; } @Column(name = "last_updated") @Temporal(TemporalType.TIMESTAMP) public Date getLastUpdated() { return lastUpdated; } public void setLastUpdated(Date lastUpdated) { this.lastUpdated = lastUpdated; } }
Repository
import java.util.List; import javax.persistence.EntityNotFoundException; import org.hibernate.Query; @Repository("clientMasterRepo") @Transactional public class ClientMasterRepoHibernate implements ClientMasterRepository { @Autowired private SessionFactory sessionFactory; @Override public ClientMaster find(Long id) { // Based on the Hibernate currentsession get the ClientMaster Object based on Id ClientMaster clientMaster = (ClientMaster) sessionFactory.getCurrentSession().get(ClientMaster.class, id); return clientMaster; } @Override public List<ClientMaster> findAll() { // Get all the ClientMaster records. Query query = sessionFactory.getCurrentSession().createQuery("FROM ClientMaster"); List<ClientMaster> clientMasterList = query.list(); return clientMasterList; } @Override public ClientMaster save(ClientMaster clientMaster) { // Insert or Update the ClientMaster object sessionFactory.getCurrentSession().saveOrUpdate(clientMaster); return CclientMaster; } @Override public void delete(ClientMaster clientMaster) { // Delete the ClientMaster object sessionFactory.getCurrentSession().delete(clientMaster); } }
JUnit Tests
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"file:src/main/resources/META-INF/spring/spring-master.xml"}) public class ClientMasterRepositoryTests { @Autowired private ClientMasterRepository clientMasterRepository; private List<ClientMaster> ClientMasterList; @Before public void setup(){ // Initialize ClientMaster mock objects for testing ClientMasterList = new ArrayList<ClientMaster>(); ClientMaster client1 = new ClientMaster(); client1.setClientName("Client1"); client1.setDateCreated(new Date()); client1.setLastUpdated(new Date()); ClientMaster client2 = new ClientMaster(); client2.setClientName("Client2"); client2.setDateCreated(new Date()); client2.setLastUpdated(new Date()); ClientMaster client3 = new ClientMaster(); client3.setClientName("Client3"); client3.setDateCreated(new Date()); client3.setLastUpdated(new Date()); ClientMaster client4 = new ClientMaster(); client4.setClientName("Client4"); client4.setDateCreated(new Date()); client4.setLastUpdated(new Date()); ClientMasterList.add(client1); ClientMasterList.add(client2); ClientMasterList.add(client3); ClientMasterList.add(client4); } @Test public void testSave(){ for (Iterator iterator = ClientMasterList.iterator(); iterator.hasNext();) { ClientMaster ClientMaster = (ClientMaster) iterator.next(); // insert the ClientMaster object clientMasterRepository.save(ClientMaster); assertTrue(ClientMaster.getClientName()+" is saved - Id "+ClientMaster.getId(),ClientMaster.getId() > 0); } } @Test public void testUpdate(){ // get a ClientMaster object from the repository ClientMaster clientMasterObj = clientMasterRepository.find(1L); // assert if its not null assertTrue(!clientMasterObj.getClientName().isEmpty()); // change the client name clientMasterObj.setClientName("Client1-Changed"); // update the ClientMaster object clientMasterRepository.save(clientMasterObj); // assert the value changed id true assertEquals("Client1-Changed", clientMasterRepository.find(1L).getClientName()); } @Test public void testDelete(){ // get a CASClientMaster object from the repository ClientMaster clientMasterObjBeforeDel = clientMasterRepository.find(2L); // delete the ClientMaster object clientMasterRepository.delete(clientMasterObjBeforeDel); // get a ClientMaster object from the repository ClientMaster clientMasterObjAfterDel = clientMasterRepository.find(2L); // get the ClientMaster object ID assertNull(clientMasterObjAfterDel); } @Test public void testFind(){ // get a ClientMaster object from the repository ClientMaster clientMasterObj = clientMasterRepository.find(3L); // compare the id's of passed and retrieved objects. assertThat(clientMasterObj.getId(), is(3L)); assertTrue(clientMasterObj.getId() == 3); } @Test public void testFindAll(){ // get all ClientMaster object from the repository List<ClientMaster> ClientMasterList = clientMasterRepository.findAll(); // check if it returns all records from DB assertTrue(ClientMasterList.size() > 0); assertThat(ClientMasterList.size(), is(4)); } }
When I execute above all test cases entirely, find & findAll test cases will be failed but pass when those executed individually. I am a newbie to testing framework. Let me know if there is anything wrong with above methodology to test the repository layer.
UPDATE
Even update test case is behaving strangely for some execution works properly in some it gives error as "NullPointerException" when i fetch the clientMaster object with find method.
-
Amit about 9 yearsekuusela - thanks for quick reply...ya i know abt (at)Transactional annotation..but clarify something for me...so data persisted in (at)Before annotated method will rolled back for first test case or once all the test cases are executed....in tht case can u suggest the better to test the repository methods..i have checked many links tht i found but none addressed the issue i am facing..
-
ekuusela about 9 yearsdocs.spring.io/spring/docs/current/spring-framework-reference/… . If a test is marked as (at)Transactional then it and the (at)Before method called before that test will run inside that transaction and any changes made in the test or the before method will be rolled back.
-
Amit about 9 yearsekuusela - thanks for your suggestions..i was able to resolve it by setting up a separate DB (HSQL) for testing...and setting
hibernate.hbm2ddl.auto
property tocreate-drop
such that for every test execution i will have a fresh DB...correct me if i am wrong...since i have configured my application to use springtransactions
if a test case is marked astransactional
does theidentity column
value will be reset to initial value after roll back?? this was the issue for me identity column values doesnt seem to reset.....is this true..or am i missing something...