如何为方法编写单元测试,该方法可以动态创建数据库表?

时间:2015-08-21 09:37:07

标签: java spring jpa

TL;博士; 即可。我有一个动态创建新数据库表的方法,我想为它编写一个单元测试。不幸的是,测试运行器在测试后没有以适当的方式执行回滚,并且在测试完成后表仍然保留在DB中。我该怎么办?

长篇故事:

我对Java Persistence和Java Spring都不是很熟悉,所以,如果你发现当前的解决方案很丑陋(对我而言,它相当难看),请告诉我如何改进它 - 我会非常感谢您的意见。

我有一个SysDatastoreService,其中包含addStaticDatastore方法的以下实现。

@Service
public class SysDatastoreServiceImpl implements SysDatastoreService {
    @Autowired
    private SysDatastoreRepository datastoreRepository;

    @Autowired
    private DataSource dataSource;

    @Override
    @Transactional
    public Optional<SysDatastore> addStaticDatastore(String name, String tableName, String ident, Long ord) {
        String createTableSql = PostgresTableSqlBuilder.createTableInPublicSchemaWithBigintPkAndFkId(
                tableName,
                SysObject.TABLE_NAME,
                Optional.of(SysObject.ID_COLUMN_NAME)).buildSql();

        Optional<SysDatastore> sysDatastore = Optional.empty();

        try(
                Connection connection = dataSource.getConnection();
                Statement statement = connection.createStatement()
        ) {
            connection.setAutoCommit(false);
            Savepoint beforeTableCreation = connection.setSavepoint();

            try {
                statement.execute(createTableSql);
                sysDatastore = Optional.ofNullable(
                        datastoreRepository.save(new SysDatastore(name, tableName, DatastoreType.STATIC, ident, ord)));
            } catch(SQLException e) {
                e.printStackTrace();
            }

            if(!sysDatastore.isPresent()) {
                connection.rollback(beforeTableCreation);
            } else {
                connection.commit();
            }
        } catch(SQLException e1) {
            e1.printStackTrace();
        }

        return sysDatastore;
    }
}

因此,正如您所看到的,我从DataSource收到新连接并尝试创建新表。成功之后,我将在SysDataStoreRepository中创建一个新条目,如果失败,我将为表创建执行回滚。

当前方法存在一些缺点,其中之一是表创建和条目插入在不同的连接上运行(我是对的吗?)。

但是我在编写单元测试时遇到了一些问题。这就是我试过的:

@Transactional(propagation = Propagation.REQUIRED)
@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/rest-servlet.xml")
public class SysDatastoreServiceTest {
    @Autowired
    private SysDatastoreService sysDatastoreService;

    @Autowired
    private DataSource dataSource;

    @Test
    public void testAddStaticDatastore() throws Exception {
        Optional<SysDatastore> sysDatastore =
                sysDatastoreService.addStaticDatastore("New static datastore", "new_datastore_table",
                                                       "NEW_DATASTORE_TABLE", 42L);

        assertTrue(sysDatastore.isPresent());
        assertEquals("New static datastore", sysDatastore.get().getName());
        assertEquals("NEW_DATASTORE_TABLE", sysDatastore.get().getIdent());
        assertEquals("new_datastore_table", sysDatastore.get().getTableName());
        assertEquals(DatastoreType.STATIC, sysDatastore.get().getDynType());
        assertEquals(42L, sysDatastore.get().getOrd().longValue());

        assertTrue(dataSource.getConnection()
                             .getMetaData()
                             .getTables(null, null, sysDatastore.get().getTableName(), null)
                             .next());
    }

这个测试看起来很简单:我只是比较所有字段,然后检查数据库是否有新表。

但是,当我运行两次或更多次时,此测试失败。查看我注意到的数据库,表new_datastore_table仍然保留在模式中。我想,由于手写事务和原始sql执行,它没有正确回滚,但我不确定。

问题:我应该如何正确地为此方法编写测试用例?并且,如果当前的方法根本错误,应该如何改变?

附注:我使用的是PostgreSQL数据库,它不能被非关系型数据库所取代。

1 个答案:

答案 0 :(得分:4)

首先,CREATE TABLE是一个DDL语句,而不是DML语句。这意味着回滚不会删除表。如果要清理数据库,则必须使用 // Do any additional setup after loading the view. __block NSMutableArray *array = [[NSMutableArray alloc] init]; [array addObject:@"Apple"]; [array addObject:@"Orange"]; [array addObject:@"Mango"]; [array addObject:@"Banana"]; But when I try to add object from JSONModelArray still showing the above 4 object only. Here is JSONModelArray code: //fetch the feed _collectionBusinessFeeds = [[CollectionBusinessManager alloc] initFromURLWithString:@"http://localhost:8888/json/collectionBusinessFinal.json" completion:^(JSONModel *model, JSONModelError *err) { for(NSDictionary *item in _collectionBusinessFeeds.collectionBusinesses){ NSLog(@"%@",item); NSString *title = [item valueForKey:@"name"]; [array addObject:title]; NSLog(@"%@",array); // here array is printing all the objects with the above 4 objects } }]; @After方法明确删除它。

但是你真的需要在PostgreSQL数据库上进行测试吗? Spring对in memory databases提供了很好的支持,默认的嵌入式数据库是HSQL,它对postgresql语法有很好的支持。如果您没有复杂的声明,它就足够了,并且可以避免使主数据库混乱(可能是破坏性的)单元测试。

您可以使用@AfterClass方法创建数据库。这是一个简单的例子:

@BeforeClass