Hibernate isolated integration tests

為{幸葍}努か 提交于 2021-01-29 02:39:01

问题


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:


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.



来源:https://stackoverflow.com/questions/53621129/hibernate-isolated-integration-tests

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!