我遇到了一个与 spock 单元测试相关的非常奇怪的关闭问题,想知道是否有人可以解释这一点。
如果我们想象一个 dao、model 和 service 如下:
interface CustomDao {
List<Integer> getIds();
Model getModelById(int id);
}
class CustomModel {
int id;
}
class CustomService {
CustomDao customDao
public List<Object> createOutputSet() {
List<Model> models = new ArrayList<Model>();
List<Integer> ids = customDao.getIds();
for (Integer id in ids) {
models.add(customDao.getModelById(id));
}
return models;
}
}
我想对 CustomService.createOutputSet 进行单元测试。我创建了以下规范:
class TestSpec extends Specification {
def 'crazy closures'() {
def mockDao = Mock(CustomDao)
def idSet = [9,10]
given: 'An initialized object'
def customService = new CustomService
customService.customDao = mockDao
when: 'createOutput is called'
def outputSet = customService.createOutputSet()
then: 'the following methods should be called'
1*mockDao.getIds() >> {
return idSet
}
for (int i=0; i<idSet.size(); i++) {
int id = idSet.get(i)
1*mockDao.getModelById(idSet.get(i)) >> {
def tmp = new Model()
int tmpId = id // idSet.get(i)
return tmp
}
}
and: 'each compute package is accurate'
2 == outputSet.size()
9 == outputSet.get(0).getId()
10 == outputSet.get(1).getId()
}
}
请注意,在这里我测试了两件事。首先,我用我的 mock 初始化 dao,验证 daos 被正确调用并返回正确的数据,然后我验证我得到了正确的输出(即“ and:
”)。
棘手的部分是 for 循环,我想在其中从模拟 dao 返回与方法参数相关的模型。在上面的例子中,如果我使用一个简单for (__ in idSet)
的,模型只返回 id 10 outputSet.get(0).getId() == outputSet.get(1).getId() == 10
:。如果我使用传统的 for 循环,并使用 设置模型idSet.get(i)
,我会得到一个IndexOutOfBoundsException
. 完成这项工作的唯一方法是检索局部变量 ( id
) 中的值并使用变量进行设置,如上所述。
我知道这与 groovy 闭包有关,我怀疑 spock 在执行它们之前将模拟调用捕获到一组闭包中,这意味着模型创建取决于闭包的外部状态。我理解为什么我会得到 IndexOutOfBoundsException,但我不明白为什么int id = idSet.get(i)
被闭包捕获而i
不是。
有什么区别?
注意:这不是实时代码,而是简化以展示我的挑战的症结所在。我不会也不会在 getIds() 和 getModelById() 上进行两次后续的 dao 调用。