I can't figure out how to make my integration tests rollback. If I insert, run tests, then query, I always get an empty table at the end.
- I insert with:
INSERT INTO POKEMON_LOCATION VALUES (1, 2, 3);
- I run
PokemonLocationDaoIT
- I query with
SELECT * FROM POKEMON_LOCATION
Some code:
PokemonLocationDaoIT.java
package com.uprr.app.tng.spring.dao;
import com.uprr.app.tng.spring.config.DefaultDaoConfig;
import com.uprr.app.tng.spring.pojo.PokemonLocation;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import javax.inject.Inject;
import javax.sql.DataSource;
import java.util.Collection;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Created by david on 9/17/16.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DefaultDaoConfig.class)
@Transactional
@Rollback
public class PokemonLocationDaoIT {
private static final Operation DELETE_POKEMON_LOCATION = deleteAllFrom("POKEMON_LOCATION");
@Inject private PokemonLocationDao testable;
@Test
public void shouldBeEmpty() throws Exception {
this.testable.deleteAll();
final Collection<PokemonLocation> all = this.testable.getAll();
assertThat(all).isEmpty();
}
}
DefaultPokemonLocationDao
package com.uprr.app.tng.spring.dao;
import com.uprr.app.tng.spring.exception.InvalidPrimaryKeyException;
import com.uprr.app.tng.spring.pojo.PokemonLocation;
import org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import javax.annotation.Nonnull;
import java.sql.SQLException;
import java.util.Collection;
/**
* Created by david on 9/17/16.
*/
public class DefaultPokemonLocationDao implements PokemonLocationDao {
@Nonnull private final JdbcTemplate jdbcTemplate;
@Nonnull private final RowCountMapper rowCountMapper;
@Nonnull private final RowMapper<PokemonLocation> rowMapper;
public DefaultPokemonLocationDao(@Nonnull final JdbcTemplate jdbcTemplate,
@Nonnull final RowCountMapper rowCountMapper,
@Nonnull final RowMapper<PokemonLocation> rowMapper) {
this.jdbcTemplate = jdbcTemplate;
this.rowCountMapper = rowCountMapper;
this.rowMapper = rowMapper;
}
@Override
public void create(@Nonnull final PokemonLocation object) {
final int id = 1; // FIXME: How to use PK generator
this.jdbcTemplate.update("INSERT INTO POKEMON_LOCATION VALUES (ID = :id, X = :x, Y = :y)", id, object.getX(),
object.getY());
}
@Override
public PokemonLocation get(@Nonnull final Integer id) {
return this.jdbcTemplate.queryForObject("SELECT * FROM POKEMON_LOCATION WHERE ID = :id", new Integer[]{id},
this.rowMapper);
}
@Override
public Collection<PokemonLocation> getAll() throws SQLException {
return this.jdbcTemplate.query("SELECT * FROM POKEMON_LOCATION", this.rowMapper);
}
@Override
public void update(@Nonnull final PokemonLocation location) {
final Integer primaryKey = location.getPrimaryKey();
if (null == primaryKey) {
throw new InvalidPrimaryKeyException("Can not update an object without a primary key. Try the save() " +
"method for creating new rows. " + location);
} else {
final String sql = "UPDATE POKEMON_LOCATION SET X = :x AND Y = :y WHERE ID = :id";
final int updatedRows = this.jdbcTemplate.update(sql, location.getX(), location.getY(), primaryKey);
if (1 != updatedRows) {
throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(sql, 1, updatedRows);
}
}
}
@Override
public void delete(@Nonnull final Integer id) {
final String sql = "DELETE FROM POKEMON_LOCATION WHERE ID = :id";
final int updatedRows = this.jdbcTemplate.update(sql, id);
if (1 != updatedRows) {
throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(sql, 1, updatedRows);
}
}
@Override
public void delete(@Nonnull final PokemonLocation object) {
final Integer pk = object.getPrimaryKey();
if (null == pk) {
throw new InvalidPrimaryKeyException("Cannot delete an object with a null primary key: " + object);
} else {
this.delete(pk);
}
}
@Override
public int deleteAll() {
return this.jdbcTemplate.update("DELETE FROM POKEMON_LOCATION");
}
@Override
public int count() {
return this.jdbcTemplate.queryForObject("SELECT count(*) FROM POKEMON_LOCATION", this.rowCountMapper);
}
}
DefaultDaoConfig.java
package com.uprr.app.tng.spring.config;
import com.uprr.app.tng.spring.dao.DefaultPokemonLocationDao;
import com.uprr.app.tng.spring.dao.PokemonLocationDao;
import com.uprr.app.tng.spring.dao.PokemonLocationMapper;
import com.uprr.app.tng.spring.dao.RowCountMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.sqlite.SQLiteConfig;
import org.sqlite.SQLiteDataSource;
import javax.inject.Inject;
import javax.sql.DataSource;
/**
* Created by david on 9/17/16.
*/
@Import(PropertiesConfig.class)
@EnableTransactionManagement
public class DefaultDaoConfig implements DaoConfig {
@Inject private Environment environment;
@Bean
@Override
public PokemonLocationDao pokemonLocationDao() {
return new DefaultPokemonLocationDao(this.jdbcTemplate(), this.rowCountMapper(), this.pokemonLocationMapper());
}
@Bean
public PokemonLocationMapper pokemonLocationMapper() {
return new PokemonLocationMapper();
}
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(this.dataSource());
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(this.dataSource());
}
@Bean
public DataSource dataSource() {
final SQLiteDataSource dataSource = new SQLiteDataSource(new SQLiteConfig());
dataSource.setUrl(this.environment.getRequiredProperty("database.jdbc.url"));
return dataSource;
}
@Bean
public RowCountMapper rowCountMapper() {
return new RowCountMapper();
}
}
The log shows that Spring is rolling the transaction back:
Sep 25, 2016 5:24:15 PM org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames
INFO: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
Sep 25, 2016 5:24:15 PM org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners
INFO: Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@64d2d351, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@1b68b9a4, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@4f9a3314, org.springframework.test.context.support.DirtiesContextTestExecutionListener@3b2c72c2, org.springframework.test.context.transaction.TransactionalTestExecutionListener@491666ad, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@176d53b2]
Sep 25, 2016 5:24:15 PM org.springframework.context.support.GenericApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericApplicationContext@f8c1ddd: startup date [Sun Sep 25 17:24:15 CDT 2016]; root of context hierarchy
Sep 25, 2016 5:24:15 PM org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
INFO: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
Sep 25, 2016 5:24:19 PM org.springframework.test.context.transaction.TransactionContext startTransaction
INFO: Began transaction (1) for test context [DefaultTestContext@2dd29a59 testClass = PokemonLocationDaoIT, testInstance = com.uprr.app.tng.spring.dao.PokemonLocationDaoIT@784c3487, testMethod = shouldBeEmpty@PokemonLocationDaoIT, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@1e636ea3 testClass = PokemonLocationDaoIT, locations = '{}', classes = '{class com.uprr.app.tng.spring.config.DefaultDaoConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]; transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@53142455]; rollback [true]
Sep 25, 2016 5:24:35 PM org.springframework.test.context.transaction.TransactionContext endTransaction
INFO: Rolled back transaction for test context [DefaultTestContext@2dd29a59 testClass = PokemonLocationDaoIT, testInstance = com.uprr.app.tng.spring.dao.PokemonLocationDaoIT@784c3487, testMethod = shouldBeEmpty@PokemonLocationDaoIT, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@1e636ea3 testClass = PokemonLocationDaoIT, locations = '{}', classes = '{class com.uprr.app.tng.spring.config.DefaultDaoConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].
So can anyone tell me why the rows are deleted after running this test?
Let me know if there is more code that you might need to see.