Skip to content

Transactional unit test support without overhead of container [SPR-7551] #12208

Closed as not planned
@spring-projects-issues

Description

@spring-projects-issues

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:

1 votes, 2 watchers

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions