这里有很多关于Transactions和JUnit的问题。但请在阅读之前仔细阅读,因为我找不到任何有同样问题的人。
我有一个商务方法,注释@Transactional
。在这种方法中,如果出现某些特殊情况,我将进行编程回滚。 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
现在让我们不要讨论编程回滚是好还是坏。让我们接受它,并接受它留在那里并使用它。
如果我启动我的应用程序并以老式的方式测试这种商务方法,那么一切都很完美。什么东西应该被回滚,它被回滚,当一切都好的时候,一切都好。而且我也在没有@Transactional
的情况下进行了测试,只是为了看到没有任何东西可以回滚,即使它应该。
一切都按计划进行。
但我遇到的问题是JUnit。目前我有2个JUnit测试此方法。 1应该失败(并触发编程回滚)和一个没有回滚的成功。
我尝试了很多不同的Junit类设置。目前它看起来像这样:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:springTestContext.xml", "classpath:springTestContext-dao.xml"})
@TransactionConfiguration(transactionManager = "txManager")
public class MyManagerTest extends AbstractTransactionalJUnit4SpringContextTests {
@Mock
private ProductDao productDao;
@InjectMocks
MyManager myManager = new MyManagerImpl();
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testUnParsableXml() {
String xml = "adlsfas";
Response response = myManager.processXMLContent(xml);
assertFalse(response.isSuccess());
System.out.println(response.getResponse());
}
}
@Service("myManager")
public class MyManagerImpl extends BaseManager implements MyManager {
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public Response processXMLContent(String xml) {
/* NB. Extremly simplified version.... */
Response response = new Response();
try {
parseXml(); // just dummy sample. Its actually parsing xml
response.setSuccess(true)
catch(SAXException e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
response.setSuccess(false);
}
return response;
}
}
springTestContext具有<tx:annotation-driven
注释,dao-context具有transactionmanager,entityfactory和数据源。
可能甚至不需要那些?因为这个测试在db中绝对没有任何关系。我想要测试的是,如果失败,则在事务中完成编程回滚。
但我添加它们的原因是因为我试图在这里获得帮助的错误。每当在商务方法中调用programaticly rollback时,我总是会得到这个错误(仅适用于junit测试,否则完全正常工作):
org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope
所以我的问题是:我做错了什么。如何让我的商务方法进行交易? 然后作为奖励问题,我如何测试在事务中调用回滚?
感谢您的时间和帮助!
答案 0 :(得分:2)
您必须使用 AbstractTraUnactionalJUnit4SpringContextTests 设置的 SpringJUnit4ClassRunner 运行测试,但是您已使用 MockitoJunitRunner 覆盖它。只需删除测试顶部的 RunWith 即可。
答案 1 :(得分:2)
您创建MyManager类MyManager myManager = new MyManagerImpl();
的新实例,而不是使用上下文中的实际bean
@Resource(name="myContext")
MyManager myManager;
显然,no proxy is being created和你所指的MyManager实例甚至不是Spring bean,因为它不是在DI容器中创建的。
对于测试类中的@Transactional
注释方法,我相信它们是用TransactionalTestExecutionListener执行的(因此机制与容器中的机制略有不同),但我认为这不应该影响容器本身的事务包装器 - 如果需要,请参阅beforeTestMethod和afterTestMethod以检查PlatformTransactionManager的操作。
在TestContext框架中,事务由管理 TransactionalTestExecutionListener,通过配置 默认情况下@TestExecutionListeners注释,即使您没有 在测试类上显式声明@TestExecutionListeners。至 启用对事务的支持,但是,您必须提供 PlatformTransactionManager bean在应用程序上下文中加载 @ContextConfiguration语义。此外,您必须申报 @Transactional在类或方法级别。
所以我认为运行测试clases的@Transactional
注释方法和实际bean之间没有那么大差别(当然,测试类的默认回滚标志除外) - TransactionAspectSupport和TransactionalTestExecutionListener只调用底层PlatformTransactionManager的方法。
P.S。至于你的集成测试,你不清楚你正在尝试模拟bean(ProductDao和MyManager) - 当我们用Spring进行集成测试时,我们测试真实bean如何交互在一起,只模拟容器外部的依赖项(例如,使用MockServletContext而不是绑定到真实的Web服务器)或用轻量级/嵌入式服务器替换重量级/生产级服务器上的依赖项 - 这是现实条件,便利性和测试执行速度之间的平衡。 / p>
答案 2 :(得分:0)
如果只想通过此测试,则可以在方法上添加@Test(expected = NoTransactionException.class)。