パラメタライズドテストのメリット解説
2019/11/29(金)
YUMEMI.swift #5 〜Free Talk〜
@the_uhooi
自己紹介
_______ _ _ ______
|__ __| | | | ____|
| | | |__| | |__
| | | __ | __|
| | | | | | |____
_ |_|_ |_| |_|______| _
| | | | | (_)
| | | | |__ ___ ___ _
| | | | '_ \ / _ \ / _ \| |
| |__| | | | | (_) | (_) | |
\____/|_| |_|\___/ \___/|_|
I am a very weak vimmer.
私はよわよわVimmerです。
メソッドをテストする
テストケースは何パターン必要?
func canWork(motivation: Int, hasRedBull: Bool) -> Bool {
if motivation < 100 {
return false
}
if !hasRedBull {
return false
}
return true
}
正解は4パターン
motivation | hasRedBull | return value |
---|---|---|
99 | true | false |
99 | false | false |
100 | true | true |
100 | false | false |
手動でテストする
一覧表に○×を付けるのが一般的
No | motivation | hasRedBull | return value | 判定 | 日付 | 担当 |
---|---|---|---|---|---|---|
1 | 99 | true | false | OK | 2019/11/29 | ウホーイ |
2 | 99 | false | false | OK | 2019/11/29 | ウホーイ |
3 | 100 | true | true | NG | 2019/11/29 | ウホーイ |
4 | 100 | false | false | OK | 2019/11/29 | ウホーイ |
自動でテストする
func test_canWork_motivation_99_hasRedBull_true() {
let logic = LogicSample()
XCTAssertFalse(logic.canWork(motivation: 99, hasRedBull: true))
}
func test_canWork_motivation_99_hasRedBull_false() {
let logic = LogicSample()
XCTAssertFalse(logic.canWork(motivation: 99, hasRedBull: false))
}
func test_canWork_motivation_100_hasRedBull_true() {
let logic = LogicSample()
XCTAssertTrue(logic.canWork(motivation: 100, hasRedBull: true))
}
func test_canWork_motivation_100_hasRedBull_false() {
let logic = LogicSample()
XCTAssertFalse(logic.canWork(motivation: 100, hasRedBull: false))
}
何も工夫していないテストコード
- 網羅できているかわかりづらい
- 同じような処理で読みづらい
- メソッド名に悩む
テストコードがわかりづらい、読みづらいと…
保守されなくなる
エクセルっぽく書きたい
motivation | hasRedBull | return value |
---|---|---|
99 | true | false |
99 | false | false |
100 | true | true |
100 | false | false |
エクセルっぽく書きたい
let tests: [(motivation: Int, hasRedBull: Bool, expect: Bool)] = [
( 99, true, false),
( 99, false, false),
(100, true, true ),
(100, false, false)
]
引数の配列を作り、ループしてテストすればいいんだ!
自動でテストする(工夫)
func test_canWork() {
let tests: [(line: UInt, motivation: Int, hasRedBull: Bool, expect: Bool)] = [
(#line, 99, true, false),
(#line, 99, false, false),
(#line, 100, true, true ),
(#line, 100, false, false)
]
for (line, motivation, hasRedBull, expect) in tests {
let logic = LogicSample()
let result = logic.canWork(motivation: motivation, hasRedBull: hasRedBull)
XCTAssertEqual(result, expect, line: line)
}
}
わかりやすい!
- 網羅できているか、ひと目でわかる
- 処理の重複がない
- テストケースを追加・変更・削除しやすい
パラメタライズドテスト(parameterized test)
このように、引数のみを切り替えて実行するテストを 「 パラメタライズドテスト 」という
言語によって呼び方が異なることがある
言語 | 名前 |
---|---|
Go | テーブル駆動テスト |
Java | パラメータ化テスト |
他言語では一般的なのに、なぜかSwiftでは知られていない…
Swiftでの実装詳細
@tobi462さんの資料がとても参考になります
Swift で ParameterizedTest をやってみた話/swift-parameterized-test - Speaker Deck
https://speakerdeck.com/yusukehosonuma/swift-parameterized-test
おまけ
Swiftでも、SpockのData Tablesのように書きたい!
class MathSpec extends Specification {
def "maximum of two numbers"(int a, int b, int c) {
expect:
Math.max(a, b) == c
where:
a | b | c
1 | 3 | 3
7 | 4 | 7
0 | 0 | 0
}
}
演算子のオーバーロードとFunction Buildersで実現できないかな…
参考
http://spockframework.org/spock/docs/1.3/data_driven_testing.html#data-tables
ご清聴ありがとうございました
______ _ _ _____
| ____| \ | | __ \
| |__ | \| | | | |
| __| | . ` | | | |
| |____| |\ | |__| |
|______|_| \_|_____/