スポックの単体テストに関連するかなり奇妙な閉鎖の問題に遭遇し、誰かがこれを説明できるかどうか疑問に思いました。
dao、モデル、およびサービスを次のように想像すると、次のようになります。
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()
}
}
ここで 2 つのことをテストしていることに注意してください。まず、モックで dao を初期化し、daos が正しく呼び出されて適切なデータを返すことを確認します。次に、適切な出力 (つまり " and:
") が得られることを確認します。
トリッキーな部分は、メソッド パラメーターに関連するモック dao からモデルを返す必要がある for ループです。上記の例で単純な を使用するfor (__ in idSet)
と、モデルは id 10: のみを返します outputSet.get(0).getId() == outputSet.get(1).getId() == 10
。従来の for ループを使用し、モデルを で設定するとidSet.get(i)
、 が得られますIndexOutOfBoundsException
。これを機能させる唯一の方法は、ローカル変数 ( ) で値を取得し、id
上記のように変数で設定することです。
私はこれが groovy クロージャーに関連していることを知っており、spock はそれらを実行する前に一連のクロージャーにモック呼び出しをキャプチャするのではないかと考えています。つまり、モデルの作成はクロージャーの外部状態に依存していることを意味します。int id = idSet.get(i)
IndexOutOfBoundsException が発生する理由は理解していますが、クロージャーによってキャプチャされないのに、なぜキャプチャされるのかわかりi
ません。
違いはなんですか?
注: これは実際のコードではなく、私の課題の要点を示すために簡略化したものです。getIds() と getModelById() で 2 つの後続の dao 呼び出しを行うことはありません。