Why won't the transaction start in my junit te

2019-06-06 00:55发布

问题:

I have a Spring 3.1 MVC + Hibernate 3.6 project with its junit4 test suit. My problem is that there is no transaction starting in my test cases, even thought I added a @Transactional.

My test case calls a controller and a dao. In the controller, a transaction is started anyway, so it does not complain. In the dao, I added a @Transactional(propagation = Propagation.MANDATORY) to be sure it will take the test's transaction. And currently it raises an IllegalTransactionStateException, which I guess it means there is no current transaction.

I tried to create programmaticaly an transaction and it does work, which means the AOP proxy to get the dao service is not the cause of the problem. However I want to create a transaction with the @Transactional annotation.

here's my dao:

// ...imports...

@Repository("taskDao")
@Transactional(propagation = Propagation.MANDATORY)
public class TaskHome implements TaskDao {

    private static final Log log = LogFactory.getLog(TaskHome.class);

    private SessionFactory sessionFactory;

    @Autowired
    public TaskHome(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public Task findById(int id) {
        log.debug("getting Task instance with id: " + id);
        try {
            Task instance = (Task) this.sessionFactory.getCurrentSession().get(
                    Task.class, id); // exception raised here!
            if (instance == null) {
                log.debug("get successful, no instance found");
            } else {
                log.debug("get successful, instance found");
            }
            return instance;
        } catch (RuntimeException re) {
            log.error("get failed", re);
            throw re;
        }
    }
    ...
}

Here's my test case:

// ...imports...

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "/test-config.xml", "/applicationContext.xml" })
@TransactionConfiguration(defaultRollback = true)
@Transactional
public class TestTaskController {

    private static ClassPathXmlApplicationContext context;
    private static TaskDao taskDao;

    @BeforeClass
    public static void initHibernate() throws Exception {
        context = new ClassPathXmlApplicationContext("applicationContext.xml");
        taskDao = context.getBean("taskDao", TaskDao.class);
    }

    @Test
    public void testOnSubmit() {

        // expects an existing default transaction here

        Task task = taskDao.findById(1); // fails already here

        // ... calls the controller and does some tests. 

    }

}

I searched in all Spring's documentation and googled it in any way I could imagine, but I don't see why the transaction is not started. Any help is very welcome.

回答1:

When using @RunWith(SpringJUnit4ClassRunner.class) you should obtain beans from the application context created by SpringJUnit4ClassRunner rather than from your own one.

In your case things go wrong because @Transactional on the unit test creates a transaction in the application context managed by SpringJUnit4ClassRunner, but you call methods on the beans obtained from the application context created manually.

So, remove your @BeforeClass method and obtain TaskDao via autowiring:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "/test-config.xml", "/applicationContext.xml" })  
@TransactionConfiguration(defaultRollback = true)
@Transactional  
public class TestTaskController {
    @Autowired
    private TaskDao taskDao; 

    ...
}

See also:

  • 9.3.5 Spring TestContext Framework