分かっているようで意外と知らない
ソフトウェアテスト

@nefrock in 大岡山

who


https://koka831.github.io/about

Kohei Kariya -> koka

Freelance Engineer

今日の内容

テストの目的
テストの種類
テストの導入
テストの運用

テストの目的

Q. “テストって何の為にするんだっけ”

“バグを未然に防ぐため”

本当に?

Q. “そもそもバグってなに?”

“仕様です! と言い張れないような挙動”

→ ユーザが被害を被るようなもの全般

テストの目的

バグを未然に防ぐ ← もちろんある

“技術的負債を増やさない” ← こっちの方が大事

WHY?

バグを未然に防ぐ

  • バグはどうしても生じる

  • バグを全て見つけるのは不可能

バグの存在は示せるがバグの不在は証明できない

“そもそもなぜテストを書くんだっけ”

  • バグを防ぐ
  • 技術的負債を増やさない

まだ表面的

→ ユーザが被害を被らないため

x 目的と手段を履き違えない

ソフトウェア開発における

テストの立ち位置

バグを減らし、バグが生じた際に修正コストを低く保つ為に技術的負債を増やさないための手段

よっしゃテスト書くで!

…あれ、どうやって書けばええんや?

バグを全て防ぐのは不可能
→ 全てを網羅するテストが書けないから

バグが発生しうるテストケースを考慮できるならバグは起こらない

テスターを無限人雇うと

→ テスターの品質・テスターのテストはどうやって保障するのか

プロダクトを理解したエンジニアが兼任するのがベスト

自分で書くしかない

テストの種類

  • ホワイトボックス

  • ブラックボックス

ブラックボックステスト

  • 入力と出力のペアを与え、期待した挙動が得られるか

→ 実装を気にしないテスト

ホワイトボックステスト

  • プログラムの構造が正しいか

→ プログラムの分岐条件各々に対してフローテスト

それぞれのデメリット

  • BB: テストケースが引数の組み合わせだけ増える
    • 機能追加と共に既存テストケースも改修する必要が生じる
  • WB: 仕様とずれていても検知できない

両方やりたいけどコストが…

“どこをテストすればいいの?”

“バグの8割はプログラムの20%の部分に集中している”

複雑な部分・バグりやすい部分は大体同じ

→ 普段からテストを書いていればここが落ちやすいというのがわかる

→ Test-Driven Development

カバレッジ計測を行うことでバグの埋まりやすい部分、テストの薄い部分が可視化される

→ ホワイトボックステスト

  • Property-Based Testingを用いることで型システムによるテストケースの自動化が可能

→ ブラックボックステスト

閑話休題

静的型付けと動的型付け

Q. “型の有無でバグ数に有意差が生じるのか”

A. ほぼない

  • 強い静的型付け言語は弱い型付けに比べやや有意
  • 静的型付け言語は動的言語に比べやや有意
  • 言語間の差はそこまでない

→ 型よりもプロダクト・チームにあった言語を採用するほうがよい

とはいっても今回は型システムの恩恵を紹介します

Property-Based Testing

型システムを用いることで(ある種の)バグの不在を証明出来る

ブラックボックステストを行う場合のつらみ

  • 改修に弱い
    テストデータも改修する必要がでてくる

Property-Based Testing

テストデータを型情報に基づいて乱数生成する

→ 入力値の組み合わせを自動生成出来る!

  • テストデータの管理が楽
  • バグを埋め込みやすい境界値ケースも生成できる
    → カバレッジと組み合わせればつよい

例(RustのProptestというライブラリ)

#[test]
fn parses_all_valid_dates(
  s in "[0-9]{4}-[0-9]{2}-[0-9]{2}"
  ) {
    println!("{}-{}/{}", y, m, d);
    // 1667-77-67
    // 5076-51-20
}

例(RustのQuickcheck)

quickcheck! {
    fn prop(xs: Vec<u32>) -> bool {
        xs == reverse(&reverse(&xs))
    }
}

関数の挙動もテストできる

  • Generatorを作成することも可能
  • enum, 構造体にも応用可能

まとめ

テストの目的

ユーザーが不利益を被らないようにする

そのために

  • バグを極力減らす
  • 技術的負債を増やさない
  • まずは今から書くコードをTDDで
  • テストと開発を同時に行う
  • ウォーターフォール・V字モデルなどのように、
    開発とテスト工程の時期が離れれば離れるほど
  • 技術的負債が増える
  • 工数見積もりが困難になる

テストの運用

  • 開発者自身がテストを書く
  • バグは局在するのでカバレッジは見ておく

最後に

ユーザに価値を届ける為にテストをしよう

  • 参考文献・リンク

  • リーン開発の現場・オーム社・Henrik Kniberg
  • カイゼン・ジャーニー・翔泳社・市谷聡啓/新井剛
  • エンジニアリング組織論への招待・技術評論社・広木大地
  • Property-Based Testing with PropEr, Erlang, and Elixir・Hebert