まわり見てるとちょいちょいこれでハマっている人がいるので、躓かなくなると良いなと思ってまとめ
さっと
spock
細かい説明は省きますが、setup cleanup がいくつかと、where というテストデータ定義をする箇所があります
結論をさっと
表と絵で
定義 | タイミング | 備考 |
---|---|---|
setupSpec | テスト [クラス] の最初で [一回] 動く | static 相当 |
setup | テスト [データ] [ごと] に最初に動く | |
cleanup | テスト [データ] [ごと] に最後に動く | |
cleanupSpec | テスト [クラス] の最後で [一回] 動く | static 相当 |
where | テスト [メソッド] の最初で [データ分] 動く | static 相当 |
緑字が static で赤字がそれ以外です、左から読んでね
で、何が問題になるの
大きく2つ
static
static 相当なので、例えば@Autowired
してる変数とかを使った初期化処理はsetupSpec/cleanupSpec
では呼べない
where
からも使えない
やりがちなので注意
実行順
多いのがこっち
特に多いのが now とかを絡めたテストデータの作成において、思った通りのテストデータが作られなくてテストがこけるケース
例えばsetup
にシステムクロックの改ざんをするようなコードを書いておいて、where
で now を含むテストデータを作ってパターンテストした場合、
テストデータを作ってからシステムクロックの改ざんという順番になるので、期待通りにならない
その場合はセットアップをsetupSpec
に移動しよう
セットアップが static 処理内で行えない場合は、例えばwhere
側のデータを{}
で囲ってexpect
側で()
をつけたりすると実行を遅延させられるけど、
テストコードでテクいことをやりたくなる場合は 大抵の場合プロダクトコード(= 設計) がおかしいので、素直にリファクタしよう
さくっとおしまいノシ
おまけ 実行順のところで挙げた例のコードイメージ
これだとmkData()
-> setClock()
の順で動く
def setup() {
setClock()
}
def test() {
expect:
sut.f(data) == exp
where:
data || exp
mkData() || ...
}
こうするか、
def setupSpec() {
setClock()
}
def test() {
expect:
sut.f(data) == exp
where:
data || exp
mkData() || ...
}
こうするとsetClock()
-> mkData()
の順で動く
def setup() {
setClock()
}
def test() {
expect:
sut.f(data()) == exp
where:
data || exp
{ mkData() } || ...
}
ただ、例えば now に関してはsut.f
内部で生成するより引数で渡して揚げる方がずっとテストしやすいので、既存の作りを見直すのに立ち戻った方が良い
def test() {
expect:
sut.f(data, date(2020, 3, 1)) == exp
where:
data || exp
mkData() || ...
}