Reputation: 103
I'm currently writing a couple of unit tests for my application, using DBUnit (2.5.3) and hibernate (5.1.0). During the writing of these tests, I came across some odd behaviour.
@Test
public void test() {
Channel channel = new Channel();
channel.setName("My Channel");
channel.setId(14L);
channel = channelRepository.save(channel);
Channel found = channelRepository.findOne(14L);
Assert.assertNotNull(found);
List<Channel> channels = channelRepository.findAll();
Assert.assertEquals(1, channels.size());
}
Running the above test, you'd expect this test to succeed. However, this test fails at the last line of code, printing that my object "channels" is an empty list. The odd thing here is that the "findOne" dóes find my saved entity, whereas the findAll fails to find any entities at all. I'm suspecting some sort of caching issue, but I can't seem to find out how to fix it.
Here are some of the classes used during the setup
The test class itself:
public class ChannelRepositoryTest extends AbstractRepositoryTest {
@Autowired
private ChannelRepository channelRepository;
@Override
protected IDataSet getDataSet() throws Exception {
return null;
}
@Test
public void test() {
Channel channel = new Channel();
channel.setName("My Channel");
channel.setId(14L);
channel = channelRepository.save(channel);
Channel found = channelRepository.findOne(14L);
Assert.assertNotNull(found);
List<Channel> channels = channelRepository.findAll();
Assert.assertEquals(1, channels.size());
}
}
The abstractRepositoryTest:
public abstract class AbstractRepositoryTest extends AbstractRTVTest {
private static final ByteArrayResource QUERY = new ByteArrayResource("SET REFERENTIAL_INTEGRITY FALSE".toString().getBytes(Charset.forName("UTF-8")));
@Autowired
private DataSource dataSource;
@Before
public void beforeEveryTest() throws Exception {
IDatabaseConnection dbConn = new DatabaseDataSourceConnection(dataSource);
ScriptUtils.executeSqlScript(dbConn.getConnection(), QUERY); // here to satisfy jenkins, otherwise we get constraint violation errors
dbConn.getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,new H2DataTypeFactory());
if (getDataSet() != null) {
DatabaseOperation.CLEAN_INSERT.execute(dbConn, getDataSet());
}
}
protected abstract IDataSet getDataSet() throws Exception;
protected FlatXmlDataSetBuilder getFlatXmlDataSetBuilder() {
return new FlatXmlDataSetBuilder().setColumnSensing(true);
}
}
The AbstractRTVTest:
@ContextConfiguration(classes = {HibernateTestConfiguration.class})
public abstract class AbstractRTVTest extends AbstractTransactionalJUnit4SpringContextTests {
}
The HibernateTestConfiguration
@Configuration
@EnableTransactionManagement
@PropertySource("classpath:application-test.properties")
@ComponentScan(basePackages = {"be.persgroep.rtv"})
public class HibernateTestConfiguration {
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "be.persgroep.rtv.domain.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
@Bean(name = "mySimpleDataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:something;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.put("hibernate.hbm2ddl.auto", "create-drop");
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.cache.use_second_level_cache", "false");
properties.put("hibernate.cache.use_query_cache", "false");
return properties;
}
@Bean
@Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
The ChannelRepository
public interface ChannelRepository extends JpaRepository<Channel, Long> {
}
The Channel is just a simple @Entity-annotated class
@Entity
@Table(name = "ZENDERS", catalog = "")
public class Channel {
private long id;
private String name;
@Id
@Column(name = "ID")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Column(name = "NAAM")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The application-test.properties
environment=local
server.port=8080
securityservice.baseUrl=
securityservice.apiKey=
spring.profiles.active=test
dpp.sec.springuser.application=RTV
# set to false to use profiles (default is actions) as granted authorities
dpp.sec.springuser.rolesAreActions=true
# set to true to allow access to authenticated users without granted authorities
dpp.sec.springuser.allowAuthenticationWithoutRoles=false
# comma seperated list of external providers: set to LDAP or LDS to only allow users from that provider
dpp.sec.springuser.externalProviderNames=LDAP,LDS
# prefix to prepend to all granted authorities (used by RoleVoter to distinguish from other security attributes)
dpp.sec.springuser.rolePrefix=AUTH_
#encrypt passwords with RSA for authentication
dpp.sec.springuser.useRsa=true
And finally, the build.gradle file:
buildscript {
repositories {
maven { url "https://plugins.gradle.org/m2/" }
maven { url "https://artifactory.persgroep.cloud/artifactory/libs-release" }
}
dependencies {
classpath "net.persgroep.gradle:persgroep-gradle-plugins:1.3.3"
classpath 'gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:1.4.17'
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
classpath "com.moowork.gradle:gradle-node-plugin:1.1.1"
classpath 'org.ajoberstar:gradle-git:1.7.2'
// classpath "com.github.iboyko.gradle.plugins:jpamodelgen-plugin:1.0.1" ?
}
ext["thymeleaf.version"] = "3.0.3.RELEASE"
ext["thymeleaf-layout-dialect.version"] = "2.1.2"
ext['hibernate.version'] = '5.1.0.Final'
}
allprojects {
repositories {
mavenCentral()
maven { url "https://artifactory.persgroep.cloud/artifactory/libs-release" }
}
apply plugin: 'net.persgroep.java'
apply plugin: 'net.persgroep.buildversion'
apply plugin: 'com.gorylenko.gradle-git-properties'
apply plugin: 'findbugs'
apply plugin: 'pmd'
apply plugin: 'jdepend'
apply plugin: 'org.ajoberstar.grgit'
findbugs {
reportsDir = file("$project.buildDir/findbugsReports")
effort = "max"
reportLevel = "high"
}
tasks.withType(FindBugs) {
reports {
xml.enabled = true
html.enabled = false
}
}
pmd {
ignoreFailures = true
}
tasks.withType(Pmd) {
reports {
xml.enabled = false
html.enabled = true
}
}
jdepend {
toolVersion = '2.9.1'
ignoreFailures = true
}
}
subprojects {
task listAllDependencies(type: DependencyReportTask) {}
apply plugin: 'net.persgroep.java'
apply plugin: 'net.persgroep.publish'
apply plugin: 'io.spring.dependency-management'
dependencies {
compile('org.springframework.boot:spring-boot-starter')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
dependencyManagement { imports { mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}") } }
}
project('validation-tests'){
dependencies {
testCompile "info.cukes:cucumber-java:${cucumberVersion}"
testCompile "info.cukes:cucumber-junit:${cucumberVersion}"
testCompile "info.cukes:cucumber-picocontainer:${cucumberVersion}"
testCompile "org.seleniumhq.selenium:selenium-java:3.4.0"
testCompile "org.seleniumhq.selenium:selenium-support:3.4.0"
testCompile "io.github.bonigarcia:webdrivermanager:1.7.1"
testCompile group: 'org.seleniumhq.selenium', name: 'selenium-chrome-driver', version: '3.4.0'
}
// Don't run these tests by default
test.enabled = project.hasProperty("browser") && project.hasProperty("environment")
//test.enabled = false
test {
systemProperties = [
browser: project.findProperty('browser')?:'chrome',
baseTestUrl: "http://rtv." + (project.findProperty('environment') ?: 'test' )+ ".persgroep.net/"
]
}
}
project('domain:model'){
dependencies {
compile group: 'commons-lang', name: 'commons-lang', version: '2.6'
compile ('be.persgroep.adv:commons-adv-model:1.1.3'){
exclude module: 'orika-core'
}
compile group: 'ma.glasnost.orika', name: 'orika-core', version: '1.5.1'
compile("org.hibernate:hibernate-java8:5.1.0.Final")
compile group: 'com.fasterxml.jackson.core', name:'jackson-annotations'
compile group: 'com.fasterxml.jackson.datatype', name:'jackson-datatype-jsr310'
}
}
project('domain:services'){
dependencies {
compile project(':domain:model')
compile("org.springframework.boot:spring-boot-starter-security:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}")
}
}
project('application'){
dependencies {
compile project(':domain:model')
compile project(':domain:services')
compile("org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}")
}
}
project('infrastructure'){
apply plugin: 'com.moowork.node'
apply plugin: 'com.moowork.gulp'
apply plugin: 'org.springframework.boot'
apply plugin: 'net.persgroep.deploy'
apply plugin: 'net.persgroep.distzip'
apply plugin: 'war'
dependencies {
compile project(':application')
compile 'org.springframework.cloud:spring-cloud-starter-oauth2:1.2.0.RELEASE'
compile("org.springframework.boot:spring-boot-starter-web")
providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
compile("org.springframework.boot:spring-boot-starter-batch")
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("org.springframework.boot:spring-boot-starter-jdbc")
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
compile("org.springframework.boot:spring-boot-starter-security")
compile("org.thymeleaf.extras:thymeleaf-extras-springsecurity4:3.0.2.RELEASE")
compile("org.thymeleaf.extras:thymeleaf-extras-java8time:3.0.0.RELEASE")
compile 'net.logstash.logback:logstash-logback-encoder:4.8'
compile "net.persgroep.utils.spring.boot:dpp-spring-boot-starter-jwt:${dppSecurity}"
compile group: 'be.persgroep.adv', name: 'commons-adv-io', version: '1.1.3'
compile group: 'com.oracle', name: 'ojdbc6', version: '11.2.0.3'
compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.6'
compile 'org.webjars:bootstrap:3.3.7'
compile 'org.samba.jcifs:jcifs:1.3.17'
compile 'io.springfox:springfox-swagger2:2.7.0'
compile 'io.springfox:springfox-swagger-ui:2.7.0'
compile "net.persgroep.service.conversion:client:1.2"
compile "net.persgroep.diocontent.images.webservice:diocontent-webservice-client:4.0.13"
compile 'org.quartz-scheduler:quartz:2.2.1'
compile group: 'c3p0', name: 'c3p0', version: '0.9.0.4' // default c3p0 does not cut it and causes pipeline to fail
compile 'com.netflix.feign:feign-core:8.18.0'
compile 'com.netflix.feign:feign-jaxb:8.18.0'
compile 'com.netflix.feign:feign-gson:8.18.0'
compile 'com.googlecode.json-simple:json-simple:1.1.1'
compile group: 'org.json', name: 'json', version: '20170516'
compile "net.persgroep.utils.monitor:status:1.4"
compile "net.persgroep.utils.monitor:monitor:1.4"
compile("com.h2database:h2:1.4.195")
testCompile("org.dbunit:dbunit:2.5.3")
}
node {
version = '6.10.3'
npmVersion = '3.10.10'
download = true
nodeModulesDir = new File(projectDir, "src/main/client")
}
gulp {
// Set the directory where gulpfile.js should be found
workDir = new File(projectDir, "src/main/client")
// Whether colors should output on the terminal
colors = true
// Whether output from Gulp should be buffered - useful when running tasks in parallel
bufferOutput = false
}
springBoot {
buildInfo()
}
processResources() {
filesMatching('**/*.properties') {
expand(project.properties)
}
}
gulp_build.dependsOn 'installGulp'
// processes your package.json before running gulp build
gulp_build.dependsOn 'npmInstall'
// runs "gulp build" as part of your gradle build
war.dependsOn gulp_build
apply from: "$rootDir/deploy.gradle"
// needed to run the application on windows because of filename issues
task pathingJar(type: Jar) {
dependsOn configurations.runtime
appendix = 'pathing'
doFirst {
manifest {
// Build the Class-Path for absolute paths based on runtime dependencies.
attributes "Class-Path": configurations.runtime.files.collect {
it.toURL().toString().replaceFirst(/file:\/+/, '/')
}.join(' ')
}
}
}
bootRun {
dependsOn pathingJar
doFirst {
// Add the compiled app classed to the classpath of the pathing jar. Probably there is a gradle variable for them?
classpath = files("$buildDir/classes/main", "$buildDir/resources/main", "$projectDir/gsp-classes", pathingJar.archivePath)
}
}
}
task wrapper(type: Wrapper) {
gradleVersion = "${gradleWrapperVersion}"
}
tasks.withType(Test) {
reports.html.destination = file("${reporting.baseDir}/${name}")
}
The few properties used in the build.gradle are located in gradle.properties:
group=be.persgroep.rtv
cucumberVersion=1.2.5
gradleWrapperVersion=3.5
springBootVersion=1.5.3.RELEASE
dppSecurity=1.3.7
Any help is greatly appreciated!
Kind regards,
Elias
Upvotes: 2
Views: 844
Reputation: 18106
It seems there is a very subtle logical mistake.
It should be:
Assert.assertEquals(1, channels.size());
Also, please check the value of the channels
variable using the debugger (for example, step-by-step debugging): it should not be empty after performing the assignment: channels = channelRepository.findAll();
.
The wrong transaction manager is used: the JPA repositories are used, therefore, the JpaTransactionManager
should be used instead of the HibernateTransactionManager
. Because the correct one is already configured by default, it is sufficient to remove the HibernateTransactionManager
bean definition (the transactionManager()
method).
Hope this helps.
Upvotes: 3
Reputation: 662
I think your problem should be solved by adding flush
after save
. Now when you getting element by id there is no any request to database because your object is present in first level cache and hibernate just returning it from Context, but when you calling findAll
hibernate is sending request for fetching data but your data base is empty because by default hibernate will send insert
before transaction commit.
Upvotes: 1