Closed as not planned
Description
Keith Donald opened SPR-7551 and commented
I prefer to unit test my data access objects without a dependency on a Spring container. Since unit tests can require test-driven transactions, support for @Transactional
test methods in this context would be preferable to working with the PlatformTransactionManager APIs.
I implemented this capability successfully using a JUnit MethodRule. For example:
public class JdbcAccountRepositoryTest {
private EmbeddedDatabase db;
private JdbcTemplate jdbcTemplate;
private JdbcAccountRepository accountRepository;
@Rule
public TransactionalMethodRule transactional = new TransactionalMethodRule();
@Before
public void setup() {
db = new GreenhouseTestDatabaseBuilder().member().connectedAccount().testData(getClass()).getDatabase();
jdbcTemplate = new JdbcTemplate(db);
accountRepository = new JdbcAccountRepository(jdbcTemplate, NoOpPasswordEncoder.getInstance(), new StubFileStorage(), "http://localhost:8080/members/{profileKey}");
}
@After
public void destroy() {
if (db != null) {
db.shutdown();
}
}
@Test
@Transactional
public void create() throws EmailAlreadyOnFileException {
Person person = new Person("Jack", "Black", "[email protected]", "foobie", Gender.Male, new LocalDate(1977, 12, 1));
Account account = accountRepository.createAccount(person);
assertEquals(3L, (long) account.getId());
assertEquals("Jack Black", account.getFullName());
assertEquals("[email protected]", account.getEmail());
assertEquals("http://localhost:8080/members/3", account.getProfileUrl());
assertEquals("http://localhost:8080/resources/profile-pics/male/small.jpg", account.getPictureUrl());
}
}
The MethodRule implementation:
package org.springframework.test.transaction;
import java.lang.reflect.Field;
import javax.sql.DataSource;
import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.ReflectionUtils;
public class TransactionalMethodRule implements MethodRule {
public Statement apply(Statement base, FrameworkMethod method, Object target) {
if (method.getAnnotation(Transactional.class) != null) {
return new TransactionalStatement(base, target);
} else {
return base;
}
}
private class TransactionalStatement extends Statement {
private final Statement base;
private final Object target;
public TransactionalStatement(Statement base, Object target) {
this.base = base;
this.target = target;
}
@Override
public void evaluate() throws Throwable {
DataSource dataSource = getDataSource();
PlatformTransactionManager tm = new DataSourceTransactionManager(dataSource);
TransactionStatus txStatus = tm.getTransaction(new DefaultTransactionDefinition());
try {
base.evaluate();
} catch (Throwable e) {
tm.rollback(txStatus);
throw e;
}
tm.commit(txStatus);
}
private DataSource getDataSource() {
Field field = ReflectionUtils.findField(target.getClass(), "db");
field.setAccessible(true);
return (DataSource) ReflectionUtils.getField(field, target);
}
}
}
Affects: 3.0.4
Issue Links:
- Introduce a TestExecutionListener for DbUnit [SPR-6593] #11259 Introduce a TestExecutionListener for DbUnit
1 votes, 2 watchers