0

I'm a little bit new to hibernate, so I started with simple things.

According to F.I.R.S.T test principles, unit tests must be I - isolated. I'm trying to apply it to integration tests for repository layer (Hibernate\JPA) using @Transactional annotation:

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = RepositoryConfig.class) @Transactional public class EmployeeRepositoryTest extends AbstractRepositoryTest { @Autowired private IEmployeeRepository employeeRepository; @Test public void saveTest() { Employee expectedEmployee = buildEmployee(1, "Parker"); employeeRepository.save(expectedEmployee); Employee actualEmployee = employeeRepository.findById(1); assertEquals(expectedEmployee, actualEmployee); } private Employee buildEmployee(long id, String name) { Employee employee = new Employee(); employee.setId(id); employee.setName(name); return employee; } } 

However, as far as two methods are performed within a transaction, hibernate does not actually perform them (as I understand it) - at least there's no line with insert in logs.

If I run data insertion by adding a script to embeded datasourse like:

INSERT INTO employee (employee_id, employee_name) VALUES (1, 'name'); 

and try to save employee with the same id but new name, the test will success. And that's the most confusing thing for me.

I saw a solution with autowiring EntityManager and calling it's flush() method. But I don't like it, since I try to write tests without being tied to Hibernate\JPA.

I also tried different flushMode, but it didn't help either.

Q1: Is there a way to make Hibernate run queries right after repository's method is called?

Q2: Is it a good practice to call EntityManager#flush in save/update/delete repository methods explicitly?

My Employee:

@Entity @Table(name = "employee") public class Employee { @Id @Column(name = "employee_id") private long id; @Column(name = "employee_name") private String name; // the rest required things (constructor, getters/setters and etc) } 

and RepositoryConfig:

@Configuration @EnableTransactionManagement @ComponentScan("org.my.package") public class RepositoryConfig { @Bean public DataSource getDataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .build(); } @Bean public JpaTransactionManager transactionManager() { return new JpaTransactionManager(); } @Bean @Autowired public HibernateTemplate getHibernateTemplate(SessionFactory sessionFactory) { return new HibernateTemplate(sessionFactory); } @Bean public LocalSessionFactoryBean getSessionFactory() { LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); sessionFactory.setDataSource(getDataSource()); sessionFactory.setPackagesToScan("org.my.package.model"); sessionFactory.setHibernateProperties(getHibernateProperties()); return sessionFactory; } private Properties getHibernateProperties() { Properties properties = new Properties(); properties.put("hibernate.dialect", "H2Dialect"); properties.put("hibernate.show_sql", true); return properties; } } 

1 Answer 1

1

You have no option but to interact with the entity manager to get these tests working as you expect - not to trigger a flush (as that can be done by calling saveAndFlush(..) method on your repository rather than just save(...)) but to clear the first level cache:

https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html#saveAndFlush-S-

@Test public void saveTest() { Employee expectedEmployee = buildEmployee(1, "Parker"); //call save and flush for immediate flush. employeeRepository.saveAndFlush(expectedEmployee); //now you will need to clear the persistence context to actually //trigger a load from the database as employee with id 1 is already //in the persistence context. //without the below you will not see a db select entityManager.clear(); Employee actualEmployee = employeeRepository.findById(1); assertEquals(expectedEmployee, actualEmployee); } 

An alternative to clearing the persistence context is to fall back to using raw JDBC to read the updated row(s).

But I don't like it, since I try to write tests without being tied to Hibernate\JPA. You are testing a persistence mechanism implemented in Hibernate\JPA and your repository is just an abstraction that is allowing you to avoid direct calls to it so this seems a slightly ridiculous statement.

Sign up to request clarification or add additional context in comments.

6 Comments

by writing tests without being tied to Hibernate\JPA I mean that I don't want to have in my tests anything belonging to Hibernate\JPA specific things. So If hypothetically I migrate to iBatis, for example, I won't need to change tests.
You are testing a JPA repository. If you want to test an iBatis repository then you'd write a new test. The best you can do in you current test is call saveAndFlush() and then - as noted in the answer fall back to using raw JDBC to read the updates which would prevent you having to call clear() on the em.
i'm afraid i didn't get your point. Just to clarify, i'm writing integration tests (using H2), from test-/behavior-driven point of view it doesn't matter what engine is running behind the scene. So, does the fact of changing framework change the expected result of my interface methods?
You don't test Interfaces. You test implementations. Right now the unit under test is a JPA repository implementation - employeeRepository - so test that. In future you might have an iBatis one so test that when you have it. Maybe you need to clarify your question.......
Yes, you're right - i test interfaces' implementations. But does it change the logic? Maybe, it's not clear for me and you could explain the next thing. I'm trying to test the repository logic in conjunction with in-memory DB. I assume it's integration tests then. Why should I care about the underlying engine?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.