【问题标题】:How do I mock local variables of a mocked class in a Spock test?如何在 Spock 测试中模拟模拟类的局部变量?
【发布时间】:2014-08-26 18:49:46
【问题描述】:

假设我在服务中有以下方法:

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 “必须指定一个非空连接” 错误或 sql 上的一个 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
    }
}

【问题讨论】:

    标签: unit-testing variables grails mocking spock


    【解决方案1】:

    您在这里尝试做的是模拟依赖项 (Sql) 的依赖项 (DataSource)。这通常会导致您无法 100% 了解SqlDataSource 对象的交互方式。如果Sql 在版本更新中更改了与Datasource 的私人交互,您必须处理这种情况。

    您应该直接使用Sql 类,而不是模拟依赖项的依赖项。为此,sql 必须是某种可以通过 DI 或方法参数接收的显式依赖项。在这种情况下,您可以像这样模拟 execute 调用(选择 Expando-Mock 的方式,但您也可以使用 Map 或 Spock 的 Mock Stuff):

    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 Manipulation。如果项目被删除,您可以配置 GORM 和 Hibernate,以自动和透明地删除项目的所有者。为此,请查看来自 GORM 的文档(尤其是 cascade part)或直接在 Hibernate docs 中查看。

    总结一下:将cascade: 'delete' 与适当的集成测试一起使用,您可以获得高度的确定性和更少的样板代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-15
      • 1970-01-01
      • 2018-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-19
      • 1970-01-01
      相关资源
      最近更新 更多