我们说我在服务中有以下方法:
private void deleteItems(List<Item> itemsToDelete) {
def sql = new Sql(dataSource)
itemsToDelete?.each { Item item ->
sql.execute("DELETE FROM owner_item WHERE item_id = ${item.id}")
item.delete(flush: true, failOnError: true)
flushDatabaseSession();
}
}
如何在ItemServiceSpec中为此方法创建测试?当我尝试它时,我得到一个DataSource &#34;必须在sql上指定一个非空的Connection&#34; 错误或 nullPointerException 。
这是我现有的测试。
@TestFor(ItemService)
@Mock([Item])
@Build([Item])
class SubjectServiceSpec extends Specification {
...
def "delete items"() {
given:
Item item1 = Item.build().save(flush: true)
Item item2 = Item.build().save(flush: true)
Item.count() == 2
DataSource mockDataSource = Mock()
service.dataSource = mockDataSource
1 * deleteItems
when:
service.deleteItems([item1, item2])
then:
Item.count() == 0
}
}
答案 0 :(得分:1)
您在这里尝试做的是模拟依赖项(DataSource
)的依赖项(Sql
)。这通常会导致您无法100%了解Sql
与DataSource
对象的交互方式。如果Sql
更改版本更新中与Datasource
的私密互动,则必须处理此情况。
不是模拟依赖项的依赖项,而是直接使用Sql
类。为此,sql
必须是某种明确的依赖关系,您可以通过DI或方法参数接收。在这种情况下,您可以像这样模拟execute
调用(选择Expando-Mock的方式,但您也可以使用Map
或来自Spock的模拟东西):
given:
def sqlMock = new Expando()
sqlMock.execute = { return 'what ever you want or nothing, because you mock a delete operation' }
service.sql = sqlMock
when:
service.deleteItems([item1, item2])
then:
assertItemsAreDeletedAndTheOwnerAsWell()
考虑整个测试用例,我认为有两个主要问题。
第一个是,当你问自己,通过嘲笑整个sql的东西,你真正得到了什么样的确定性。在这种情况下,您在这里唯一要做的就是与db进行交互。当你嘲笑这件事时,你就无法测试了。没有多少有条件的东西或任何应由单元测试备份的东西。因此,我建议只为此测试用例编写集成规范,在这种测试用例中,您可以使用H2DB进行测试。
第二件事是,你实际上根本不需要Sql操作。如果项目被删除,您可以通过自动和透明删除项目所有者的方式配置GORM和Hibernate。为此,请查看GORM中的文档(尤其是cascade part)或直接在Hibernate docs中查看文档。
总结一下:使用cascade: 'delete'
和正确的集成测试,您可以获得更多的确定性和更少的样板代码。