在Spring 4.1之前我们在准备测试数据时可能通过继承AbstractTransactionalJUnit4SpringContextTests,然后调用executeSqlScript()进行测试,其中存在一个主要问题:如果要同时执行多个数据源的初始化就靠不住了,而且使用起来也不是特别便利,Spring4.1提供了@Sql注解来完成这个任务。
1、初始化Spring配置:
- <jdbc:embedded-database id="dataSource1" type="HSQL"/>
- <jdbc:embedded-database id="dataSource2" type="HSQL"/>
- <bean id="txManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource1"/>
- </bean>
- <bean id="txManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource2"/>
- </bean>
此处使用jdbc:embedded嵌入数据库来完成测试,数据库使用HSQL。
2、 方法级别的@Sql
- @RunWith(SpringJUnit4ClassRunner.class)
- @FixMethodOrder(MethodSorters.NAME_ASCENDING)
- @ContextConfiguration(value = "classpath:spring-datasource.xml")
- @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
- public class MethodLevelSqlTest {
- private JdbcTemplate jdbcTemplate1;
- private JdbcTemplate jdbcTemplate2;
- @Autowired
- @Qualifier("dataSource1")
- public void setDataSource1(DataSource dataSource1) {
- this.jdbcTemplate1 = new JdbcTemplate(dataSource1);
- }
- @Autowired
- @Qualifier("dataSource2")
- public void setDataSource2(DataSource dataSource2) {
- this.jdbcTemplate2 = new JdbcTemplate(dataSource2);
- }
- @Test
- @Sql(value = {"classpath:schema.sql", "classpath:init-data.sql", "classpath:updated-data.sql"},
- config =
- @SqlConfig(encoding = "utf-8", separator = ";", commentPrefix = "--",
- dataSource = "dataSource1", transactionManager = "txManager1"))
- public void test01_simple() {
- Assert.assertEquals(
- Integer.valueOf(3),
- jdbcTemplate1.queryForObject("select count(1) from users", Integer.class));
- }
- @Test
- @Sql(value = {"classpath:schema.sql", "classpath:init-data.sql"},
- config =
- @SqlConfig(encoding = "utf-8", separator = ";", commentPrefix = "--",
- dataSource = "dataSource2", transactionManager = "txManager2"))
- public void test02_simple() {
- Assert.assertEquals(
- Integer.valueOf(2),
- jdbcTemplate2.queryForObject("select count(1) from users", Integer.class));
- }
- }
方法级别的@Sql在每个方法上都会执行。其中@Sql可以指定多个sql文件,然后通过@SqlConfig指定其编码、分隔符、注释前缀、使用哪个数据源和事务管理器。
3、类级别的@Sql
- @RunWith(SpringJUnit4ClassRunner.class)
- @FixMethodOrder(MethodSorters.NAME_ASCENDING)
- @ContextConfiguration(value = "classpath:spring-datasource.xml")
- @Sql(value = {"classpath:schema.sql", "classpath:init-data.sql", "classpath:updated-data.sql"},
- config =
- @SqlConfig(encoding = "utf-8", separator = ";", commentPrefix = "--",
- dataSource = "dataSource1", transactionManager = "txManager1"))
- @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
- public class ClassLevelSqlTest {
- private JdbcTemplate jdbcTemplate1;
- @Autowired
- @Qualifier("dataSource1")
- public void setDataSource1(DataSource dataSource1) {
- this.jdbcTemplate1 = new JdbcTemplate(dataSource1);
- }
- @Test
- public void test01_simple() {
- Assert.assertEquals(
- Integer.valueOf(3),
- jdbcTemplate1.queryForObject("select count(1) from users", Integer.class));
- }
- }
类级别的对整个测试用例中的每个方法都起作用。
4、指定多个@Sql
Java8之前需要使用@SqlGroup,而Java8之后直接使用多个@Sql注解即可。
- @RunWith(SpringJUnit4ClassRunner.class)
- @FixMethodOrder(MethodSorters.NAME_ASCENDING)
- @ContextConfiguration(value = "classpath:spring-datasource.xml")
- @Transactional()
- @SqlGroup(
- {
- @Sql(value = {"classpath:schema.sql", "classpath:init-data.sql", "classpath:updated-data.sql"},
- config =
- @SqlConfig(encoding = "utf-8", separator = ";", commentPrefix = "--",
- dataSource = "dataSource1", transactionManager = "txManager1")),
- @Sql(value = {"classpath:schema.sql", "classpath:init-data.sql"},
- config =
- @SqlConfig(encoding = "utf-8", separator = ";", commentPrefix = "--",
- dataSource = "dataSource2", transactionManager = "txManager2"))
- }
- )
- @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
- public class ClassLevelSqlGroupTest {
- private JdbcTemplate jdbcTemplate1;
- private JdbcTemplate jdbcTemplate2;
- @Autowired
- @Qualifier("dataSource1")
- public void setDataSource1(DataSource dataSource1) {
- this.jdbcTemplate1 = new JdbcTemplate(dataSource1);
- }
- @Autowired
- @Qualifier("dataSource2")
- public void setDataSource2(DataSource dataSource2) {
- this.jdbcTemplate2 = new JdbcTemplate(dataSource2);
- }
- @Test
- @Transactional()
- @Sql(value = {"classpath:schema.sql", "classpath:init-data.sql", "classpath:updated-data.sql"},
- config =
- @SqlConfig(encoding = "utf-8", separator = ";", commentPrefix = "--",
- dataSource = "dataSource1", transactionManager = "txManager1"))
- public void test01_simple() {
- Assert.assertEquals(
- Integer.valueOf(3),
- jdbcTemplate1.queryForObject("select count(1) from users", Integer.class));
- }
- @Test
- @Transactional()
- @Sql(value = {"classpath:schema.sql", "classpath:init-data.sql"},
- config =
- @SqlConfig(encoding = "utf-8", separator = ";", commentPrefix = "--",
- dataSource = "dataSource2", transactionManager = "txManager2"))
- public void test02_simple() {
- Assert.assertEquals(
- Integer.valueOf(2),
- jdbcTemplate2.queryForObject("select count(1) from users", Integer.class));
- }
- }
也可以通过元注解把注解合并:
- @SqlGroup(
- {
- @Sql(value = {"classpath:schema.sql", "classpath:init-data.sql", "classpath:updated-data.sql"},
- config =
- @SqlConfig(encoding = "utf-8", separator = ";", commentPrefix = "--",
- dataSource = "dataSource1", transactionManager = "txManager1")),
- @Sql(value = {"classpath:schema.sql", "classpath:init-data.sql"},
- config =
- @SqlConfig(encoding = "utf-8", separator = ";", commentPrefix = "--",
- dataSource = "dataSource2", transactionManager = "txManager2"))
- }
- )
- @Retention(RUNTIME)
- @Target(TYPE)
- @interface CompositeSqlGroup {
- }
直接使用@CompositeSqlGroup注解即可。
5、事务
- @RunWith(SpringJUnit4ClassRunner.class)
- @FixMethodOrder(MethodSorters.NAME_ASCENDING)
- @ContextConfiguration(value = "classpath:spring-datasource.xml")
- @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
- public class TransactionalTest {
- private JdbcTemplate jdbcTemplate1;
- private JdbcTemplate jdbcTemplate2;
- @Autowired
- @Qualifier("dataSource1")
- public void setDataSource1(DataSource dataSource1) {
- this.jdbcTemplate1 = new JdbcTemplate(dataSource1);
- }
- @Autowired
- @Qualifier("dataSource2")
- public void setDataSource2(DataSource dataSource2) {
- this.jdbcTemplate2 = new JdbcTemplate(dataSource2);
- }
- @Test
- @Transactional("txManager1")
- @Sql(value = {"classpath:schema.sql", "classpath:init-data.sql", "classpath:updated-data.sql"},
- config =
- @SqlConfig(encoding = "utf-8", separator = ";", commentPrefix = "--",
- dataSource = "dataSource1", transactionManager = "txManager1"))
- public void test01_simple() {
- //判断是在事务中执行
- Assert.assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
- Assert.assertTrue(TestTransaction.isActive());
- Assert.assertEquals(
- Integer.valueOf(3),
- jdbcTemplate1.queryForObject("select count(1) from users", Integer.class));
- }
- @Test
- @Transactional("txManager2")
- @Sql(value = {"classpath:schema.sql", "classpath:init-data.sql"},
- config =
- @SqlConfig(encoding = "utf-8", separator = ";", commentPrefix = "--",
- dataSource = "dataSource2", transactionManager = "txManager2"))
- public void test02_simple() {
- Assert.assertEquals(
- Integer.valueOf(2),
- jdbcTemplate2.queryForObject("select count(1) from users", Integer.class));
- }
- @Test
- @Transactional("txManager2")
- @Sql(value = {"classpath:schema.sql", "classpath:init-data.sql"},
- config =
- @SqlConfig(encoding = "utf-8", separator = ";", commentPrefix = "--",
- dataSource = "dataSource2", transactionManager = "txManager2"))
- public void test03_simple() {
- Assert.assertEquals(
- Integer.valueOf(2),
- jdbcTemplate2.queryForObject("select count(1) from users", Integer.class));
- TestTransaction.flagForRollback();
- }
- @Rule
- public TestName testName = new TestName();
- @AfterTransaction
- public void afterTransaction() {
- System.out.println(testName.getMethodName());
- if("test03_simple".equals(testName.getMethodName())) {
- Assert.assertEquals(
- Integer.valueOf(0),
- jdbcTemplate2.queryForObject("select count(1) from users", Integer.class));
- }
- }
- }
可以使用//判断是在事务中执行TransactionSynchronizationManager.isActualTransactionActive()或TestTransaction.isActive()来判断是否是在事务中执行;通过TestTransaction.flagForRollback();来回滚事务;在测试用例中@AfterTransaction来断言回滚后数据没有插入。
本文转自http://jinnianshilongnian.iteye.com/blog/2106184