「リバースモデリングを行ってテスト設計する」は、単純にリバースしてないと気付いた話 #WACATE

はじめに

先日、WACATE 2024 夏というワークショップを開催しました。私はWACATE実行委員長として運営に携わり、準備を行ってきました。

WACATE 2024 夏の開催内容およびWACATEという団体について詳しくは、以下のページをご覧ください。

wacate.jp

WACATE 2024 夏では「テストケースには、必ず作った人の意図が存在する」というサブタイトルを付け、テスト実装からテスト分析へとテストプロセスをさかのぼる(=リバースする)過程を体験してもらいました。

ワークショップを作成するにあたり、テストプロセスについて今までより深く考えられた(気がする)ので、本記事で言語化して書き残しておきたいと思います。

前提知識:テストプロセスとは何か

ISTQB(ソフトウェアテスト技術者資格認定)では以下のようにテストプロセスを定義しています。

ISTQBテスト技術者資格制度 Foundation Level シラバス 日本語版 Version 2018V3.1.J03を参考に作成

今まで「テスト実行」と「テスト実行のための準備」ぐらいにしか分けていなかったテストのプロセスを、JSTQBではいくつものプロセスに分けて表現しています。

その中でも、「テスト分析」「テスト設計」「テスト実装」「テスト実行」は、単一のテスト対象に対してでも活用できる部分だと思っています。*1

この4つのテストプロセスのうち、テスト実行を除いた3つに着目すると、以下のようなインプット/アウトプットで作成できるでしょう。

各テストプロセスとそのインプット/アウトプットの例

本記事では、上の画像で表したテストプロセスとインプット/アウトプットを用いて説明します*2

テストプロセスをリバースするとはどういうことか

今回のWACATE 2024 夏では、前提知識で書いたテストプロセスのうち、「テスト手順書(テスト実装の成果物)」しか手元にない場合に、テスト設計やテスト分析にさかのぼる(=リバースする)ことを参加者に体験してもらおうと考えました。

この時、どのようにリバースすれば良いのでしょうか?単純に逆の順番で行えばできるのでしょうか?

逆順に行えばできる?

上の図の赤矢印のように辿ると、「テスト手順書からテスト実装を行うことで、テスト設計成果物を作成できる」と読み取れます。果たして本当にそんなことが可能なのでしょうか?私は違うと思っています。

少なくとも、「テスト手順書をインプットとしてテスト設計を再度行い、テスト設計成果物を作成する」ということになると思います。

テスト手順書(テスト実装成果物)からテスト設計成果物をリバースする流れ

「テスト設計」を細分化する

「テスト設計を行う」という部分をもっときちんと考えるため、テスト設計というプロセスをさらに細分化することを試みます。すると以下のように考えることができました。

テスト設計のプロセスの細分化

上図のうち青囲み部分が、元々「テスト設計」と呼んでいた部分です。今回は「テスト設計技法選択」「テストモデリング」「カバレッジアイテムの導出」という3つに細分化できました。

テストモデリングとは何か?

テストモデリングとは、テスト設計技法などを用いて、テストの視点で図式化、視覚化する行為のことです。詳しくは、今回の WACATE 2024 夏での発表資料をご覧ください。

speakerdeck.com

カバレッジアイテムとは何か?

JSTQBの用語集によると、以下のように書かれています。

テスト技法を使用して、一つ以上のテスト条件から導出される属性または属性の組み合わせ。テスト実行の完全性を測定するために使用する

例えば、同値分割法におけるカバレッジアイテムは代表値になります。境界値分析におけるカバレッジアイテムは境界値になります。デシジョンテーブルにおけるカバレッジアイテムは、出てきたパターンになるでしょう*3

各テスト技法とカバレッジアイテムの関係性

上図の赤丸の部分がカバレッジアイテムになります。

そして、出てきたカバレッジアイテムを元にテスト手順書を作成していると考えることができます。例えば、同値分割というテストモデリングによって同値パーティションを作成し、それを元に境界値分析を行うことで境界値を見つけ出し、その境界値を用いてのテスト実装を行うことでテスト手順書のケース1つが作成できると考えられます。

改めて、テストプロセスのリバースを考える

テスト設計というプロセスを細分化したうえで、改めてテストプロセスのリバースを考えます。すると、テスト手順書からカバレッジアイテムを導き出すのではなく、それ以前の「テスト設計技法の選択」や「テストモデリング」から行っていると思われます。

テスト手順書(テスト実装成果物)からカバレッジアイテムをリバースする流れ

つまり直前のプロセスに戻れば良いのではなく、結局はテスト設計のプロセスの初めから行っているのです。

しかし、元々のテストプロセスには(当然ながら)「テスト手順書」と「テスト設計技法選択」を結ぶ線は存在しません。つまり、ここはリバースモデリングの実施者が自ら想定しなくてはいけない部分なのです。ここは完全に想像の世界になってしまうので、ましてや前任者といった自分ではない他人が作った場合、元々の想定と違うテストモデルができる可能性が大いにある部分です。これこそがリバースモデリングする上で大変な部分でしょう。

おわりに:そもそもテストプロセスをリバースするのはアンチパターン

本記事では、テスト実装の成果物であるテスト手順書からテスト設計の成果物をリバースして作る際のプロセスについて書きました。

しかし、そもそもリバースしてテスト設計の成果物を作ること自体がアンチパターンだと思います*4

テスト実装の成果物以外は何もないところからテスト設計を想定しろということ自体が無理難題です。WACATE 2024 夏では、リバースすること自体の難しさを参加者に体験してもらいました。その経験を反面教師にして、きちんとテストプロセスに沿った成果物を作成することが大事だと理解してもらえたのではないかと思います。

なお、テストプロセスに沿った成果物について、個人的には「テスト分析」の成果物を残しておくことをオススメしています。一般的に、成果物の作成されやすさは、

テスト実装成果物(テスト手順書など)>テスト設計成果物>テスト分析成果物(テスト条件)

の順ですが、個人的には

テスト分析成果物(テスト条件)>テスト設計成果物>テスト実装成果物(テスト手順書など)

の順で残しておくべきだと思っています。なぜならば、後追いでテスト作成の経緯を追うときに、同じ思考順にできる(リバースして考えることがなくなる)ため、再現性のあるテストに向かいやすいと思っているからです*5

本記事を通じて、テストプロセスをきちんと理解することが少しでも大切だと感じてもらえれば幸いです。

参考:テストプロセスを用いて、テストケース作成の思考を整理しよう(WACATE 2022 冬の発表資料)

speakerdeck.com

*1:逆に、「テスト計画」「テストのモニタリングとコントロール」「テスト完了」は、複数のテストを行う際に効果をより発揮できるプロセスだと感じています

*2:なお、この中で「テスト条件」が馴染みのない言葉かもしれません。本記事上では「何をテストしたいのかを書き記したもの」ぐらいに捉えておいてください。詳しく知りたい方は、JSTQB FLのシラバスをご覧ください。

*3:カバレッジ」という単語から「テストコードを実行した際に測定できるコードカバレッジ」をイメージされる方もいると思います。コードカバレッジはコード1行1行がカバレッジアイテムとみなすことができるでしょう。

*4:アンチパターンである旨は、WACATE 2024 夏の中でもお伝えしました

*5:特に、テスト設計を学んでいればいるほど、再現率は高くなると思います

QAエンジニアから見た『データモデリングでドメインを駆動する』書評

はじめに

本記事は、今年発売された書籍『データモデリングドメインを駆動する――分散/疎結合な基幹系システムに向けて』を読んだ感想と、QAエンジニアである私*1が日々の業務で役立ちそう(既に役立った)部分を紹介します。今のところ、本書籍は2024年のベストバイな気がします。

gihyo.jp

本記事で一番伝えたいこと

  • データモデリングについての考えが深まるぞ
  • 開発者が読むともっと役立てることができると思うぞ
  • QAエンジニアである私が読んでも役立つぞ

読み始めてすぐに「良い買い物だった」と思って思わずポストしている様子

目次

本書籍で良かったこと:データモデリングをするにあたっての整理と用語の提案がすごい

本書籍では、データモデリングをするにあたっての思考の整理が素晴らしかったです。また、整理するにあたり、筆者の考えを元にした様々な用語の提案がされています。その提案は具体例を添えて語っており非常に分かりやすく、かつ納得のいく提案に感じました。本記事では、特に響いた部分を記載します。

SoAとSoMという整理

一般的に、「SoE(Systems of Engagement、関わり合いのシステム)」と「SoR(Systems of Record、記録のシステム)」という区分がよく聞かれます。SoEとSoRの詳細な説明は他記事に任せるとして(ググれば出てきます)、本書籍ではSoRをさらに「SoA(Systems of Activities、活動のシステム)」と「SoM(Systems of Management、経営管理のシステム)」に分けて整理しています。このSoAとSoMという考え方が、書籍全体の核となっています。

書籍の中で、「なぜSoRをSoAとSoMに分けて考えるべきなのか」に明確に答えています。書籍内に書かれている分かりやすい題材を元に、私は「SoAとSoMは、関心を持っている対象も、データとしての表現の仕方も異なっている」ということを理解できました。

詳しくは書籍を読んでもらえればと思います。書籍全体で語っているものの、第4章と第5章あたりを読むと特に理解できるかもしれません。

「残」という概念

筆者は、書籍の中で「残」という概念を提案しています。書籍を読んで理解した範囲内で、「残」について説明してみたいと思います。以下で載せている例は、書籍内の表現ではなく、私が勝手に例示している点にご注意ください。

例えば、ECサイトで何か商品を購入した場合を考えてみます。この時、クレジットカード会社から入金がされた時点でECサイトの運営会社においてはお金について作業が残っていません。これを「お金の残が無い」と表現します*2。一方で、購入が発生した時点ではまた商品のお届けがされておらず、商品の残作業があります。対面の販売のようにお金と商品がリアルタイムで交換されているわけではないからです。この時、「注文商品については残がまだある」と表現しています。

このように、1つの注文を見ても、商品にはタイムラグが生じます。本書籍では、これを「残」という概念で表現しています。また、注文という部分以外にも目を向けると、在庫管理についても「残」が発生するかもしれません*3。この「残」の管理をする(「残」が発生してから解消されるまでを管理する)ために、システムを導入しているといえるのです。

私の拙い説明ではイメージしづらいかもしれません。ただ個人的には、書籍を読んで、この「残」という概念は非常に腹落ちしました。書籍では私の数倍うまく説明していますので、詳しくは書籍を読んでください。第4章あたりに書いてあります。

データベース設計とは違う「データモデリング」という考え方

一般的に「どのようにデータを扱うか」と考えると、データベース設計に注目しがちです。しかし、本書籍では、データベース設計とは別の「データモデリング」という考え方を丁寧に説明しています。あくまでもデータモデリングは帳簿のデザインであると解説しています。それにより、データベース設計で発生する技術的関心事と分離を行うこともできます。

しかし同時に、本書籍では「データベース設計が不要である」とは主張していません。どのように使い分けて行なっていくのかについても本書籍では丁寧に説明していますので、詳しくは書籍を読んでください。第3章や第11章あたりと第8章の一部に書いてあります。

QAエンジニアとして、業務に役立てそうなこと

私はQAエンジニアです。開発者とは違い、システム自体を開発していません。そんな私にとって、「どのように開発者が設計をしているのか」という部分以外に、本書籍の内容が役立てる(応用できる)部分があるように感じました。それを述べていきます。

「業務プロセスは業務機能を横断する」をシステムテストに応用する

「2.2 活動のシステム(SoA)は現場活動を支える」の中では以下のように解説しています。

業務機能の分割は「残」に基づく

業務プロセスは業務機能を横断する

これをテストレベルに当てはめると、業務プロセスのテストがシステムテストレベルに当てはまるように感じました。業務機能の分割を行い、テストをした上で、業務プロセスのテストを行うのは理にかなっていそうな気がしました。特に図2-2-2のような表現は勉強になりました。

書籍内の図2-2-2

データモデリング作成時の思考とテスト要求分析時の思考は似ている

「3.2 ユーザーインタフェース設計だけでは帳簿をデザインできない」の中で以下のように述べています。

画面や帳簿のデザインは、業務の流れ(ユースケース)に依存します。一方で、帳簿の構造は、基本的には業務の流れに依存しないようにデザインするほうが良いのです。

画面や帳票の設計は帳簿デザインではないと述べましたが、逆に帳簿のデザイン--データモデリング--に際して、画面や帳簿のスケッチを利用することは、有用ですし、多くの場合、極めて重要でさえあります。

これは私がテスト要求分析を行う時の思考に似ていると感じました。私が行なっているテスト要求分析の考え方については以下をご覧ください。

speakerdeck.com

イケてない部分

ここまで良かったことを書きましたが、イケてないと感じた部分も書いておきます。ただしどれも技術的におかしいことを書いているわけではなく、言いたいことをうまく伝えきれてないなーと感じた部分です。

「帳簿」という言葉から受け取るイメージに乖離がある

私が商業に詳しくないが故かもしれませんが、私の中では「帳簿=会計帳簿」というイメージが強く、「会計帳簿以外にも帳簿の概念がある」ということを理解するまで時間がかかりました。

「データモデリング」と「データベース設計」の区別が付いていない読者へのフォローが少ない

「データモデリングドメインを駆動する」という書籍名だけだと、「データベース設計の本かな?」と想像してしまいました。データベース設計の本だと感じて手に取らない人が出てきたら勿体ないと感じました。

データベース設計とは違うということは「はじめに」で1行だけ述べており、その後は第3章まで読まないと理解できませんでした。例えば「1.2 データモデリングはなぜ必要なのか」でフォローしても良いように感じました。

第5章の説明が難解

第5章「経営管理のシステム(SoM)」が他の章に比べて少し読みづらかったです。

この点については筆者も認識している点なので、ここでは詳しく言及しません。

おわりに

最後に「イケてない部分」を書きましたが、総じて非常に良い本だと感じました。今のところ、2024年のベストバイな気がします。

QAエンジニアにとっても、実装から目を離し、業務から見つめ直せる点で、有用だと思います。

ちなみに、5/28に本書籍の読書感想会のイベントがあるようなので、気になる方はこちらもぜひご参加くださいませ!*4

kichijojipm.connpass.com

*1:厳密にいうと、役立ったのはテストエンジニアリングの部分

*2:エンドユーザーからすると、クレジットカードの引き落としが発生するまではお金の残があるかもしれませんが

*3:出荷指示から出荷までの間には「残」があるでしょうし、製造指示から製造完了までも「残」があるでしょう

*4:私は発表者ではないけど

Cucumberコミュニティの消滅の危機はGherkin(Given / When / Then)記法の消滅の危機でもある

はじめに

先日、Nextbeat Tech Bar:第一回ソフトウェアテストについて考える会にて、「BDD(Cucumber)コミュニティが無料提供しているコンテンツの紹介と現在起きている危機」というタイトルの発表をしました。

発表資料は以下です。

speakerdeck.com

本記事では、発表時にはうまく伝え切れなかった部分を補足として書くことを目的としています。特に、Cucumberコミュニティ消滅の危機は、Gherkin(Given / When / Then)記法の消滅の危機にも繋がっているので、Cucumberを使っていない人にも影響があるということを理解していただきたいです。

目次

本記事で言いたいことを3行で

  • Gherkin記法はCucumberコミュニティがメンテナンスしている
  • Cucumberコミュニティが資金難で消滅してしまうと、Gherkin記法もメンテナンスされなくなる
  • Gherkin記法の標準がなくなってしまう恐れがある

Gherkin記法とは何か

Gherkin記法とは、主にGiven / When / Thenで書かれた、シナリオ記述フォーマットの1つです。

例えば、以下のように記述します。

Feature: 自動販売機

    Scenario: 飲み物を買うと、飲み物代を引いた金額のお釣りが出る
        Given 自動販売機がある
        When 550 円を入れる
        And 120 円の "コーラ" を選択する
        Then "コーラ" が出てくる
        And 430 円が出てくる

Cucumberを使っていなかったとしても、Given / When / Thenが書いたシナリオを使っている人はいるのではないでしょうか?

Gherkin記法はCucumberコミュニティがメンテナンスしている

Gherkin記法は、Cucumberコミュニティの配下にある以下のリポジトリ上でメンテナンスしています。

github.com

このリポジトリ上では、記述ルールの定義や、各プログラミング言語の対応や、各自然言語の対応をしています。

プログラミング言語ごとにディレクトリを分けて管理している

gherkin-languages.json内で、各自然言語でも使えるようにしている

Cucumberコミュニティが資金難で消滅してしまうと、Gherkin記法もメンテナンスされなくなる

今回の発表でも示したとおり、現在Cucumberコミュニティは資金難に陥っています。

もしもコミュニティが消滅してしまうと、Cucumberというツールのメンテナンス以外にも、BDDの学習コンテンツ*1のメンテナンスや、Gherkin記法のメンテナンスがされなくなる恐れがあります。

Gherkin記法の標準がなくなってしまう恐れがある

実際に、Cucumberコミュニティが消滅する時には、きっと他コミュニテイがGherkinのリポジトリのメンテナンスを引き受けるでしょう。

しかしメンテナンスを引き受けることが叶わなくなった場合、Gherkin記法の標準が無くなってしまいます。

そうなると、以下のようなことが発生するでしょう。

  • 今までGherkin記法を用いていた各サービスが独自の使い方をするようになる
  • Gherkin記法に関する更新を入れたい場合には、Gherkin記法を用いたサービス全てに変更を入れる必要が出てくる
    • 例)Gherkin記法の用語の日本語訳を追加したい
  • とあるGherkin記法を用いたサービスから、別のGherkin記法を用いたサービスへ移行しようとすると、うまく動かなくなる部分が出てくる

このように、Cucumberコミュニティの存続の危機は、Gherkin記法の混乱にもつながる可能性があるのです。

おわりに

本記事では、Cucumberコミュニティの存続の危機が、Cucumberというツールのみならず、Gherkin記法にも影響するという話を書きました。

また、CucumberコミュニティはGherkin記法以外にも、BDDの発展や正しい理解にも一役買っています。なので、そういう面でもコミュニティ消滅を避けられると良いなと思い、今回の発表および本記事を書きました。

ここまでの内容を見て、少しでも貢献したいという方はぜひ寄付をお願いします。単発の寄付でもOKです。以下のページで寄付を募っていますのでよろしくお願いします!

opencollective.com

*1:Cucumberコミュニティは無料のBDDの教育コンテンツを本当に充実させています!例えばCucumber Schoolはその1つです。

school.cucumber.io

Googleスライドで紙芝居的に強調する場所を示すコツ

はじめに

先日、Developers Summitで講演してきました*1

event.shoeisha.jp

発表スライドはこちら

speakerdeck.com

このスライドを作成する際に使った、紙芝居的に強調する方法をご紹介します。完成形はこんな感じです。発表スライドのp9とp10が該当します。

なお、今回の元画像はryuzeeさんのプロダクトマネジメントの”罠”を回避しよう のスライドから拝借しています。

作り方

1.強調する前の画像を作成する。

2.画像をクリップボードにコピーする

3.半透明のオブジェクトを作成する

Googleスライドでは、透明度も含めてカラーコードが指定できます。私の場合、黒背景の半透明の場合(今回の場合)は「#000000bf」を、白背景の半透明の場合は「#ffffffbf」を指定しています。

黒背景の半透明のオブジェクトを画像の上に貼った状態

4.手順2でコピーした画像を貼り付ける

5.手順4で貼り付けた画像と手順1の画像の位置を揃える(ガイドが表示されるので、簡単に揃えられるはずです)

6.【今回のポイント!】画像を選択し、メニューバーにある「画像を切り抜く」のアイコンをクリックする

「画像を切り抜く」のアイコン

7.【今回のポイント!】強調したい場所のみに範囲を狭める

8.範囲を狭めたオブジェクトの外枠に色を付ける

完成!

おわりに

今回は紙芝居的に強調する方法をご紹介しました。

今回の場合以外にも、「画像を切り抜く」は様々な装飾に活用できますので、ぜひお試しあれ!

*1:ちなみに、今回のDevelopers Summitではもう一つセッションを持っていました。そちらについては別記事をあげていますので、気になる方はそちらもどうぞ!

nihonbuson.hatenadiary.jp

Developers Summitの新人研修セッションの中で「新人研修マニフェスト」を発表しました #devsumi

はじめに

先日、Developers Summitで、及部さんやっとむさんと一緒に新人研修に関するパネルセッションを行いました*1

event.shoeisha.jp

その冒頭で、新人研修マニフェストを発表したので、ここでも紹介します。

新人研修マニフェスト

新人研修マニフェスト

テキストにもしておきます。

  • 新人研修の充実よりもエンジニア人生の充実を
  • 包括的なカリキュラムよりも学び方を学ぶことを
  • 教えるよりも一緒に学ぶ関係性を
  • 計画に従うことよりもリアルな現場体験を

価値とする。
左記のことがらに価値があることを認めつつ、わたしたちは右記のことがらにより価値をおいています。

マニフェスト誕生の経緯

このマニフェストは、今回の新人研修セッションを行うにあたり、事前の打ち合わせの内容をもとに及部さんが作成したものです*2

当日にパネルディスカッションをやっていても、各登壇者がこのマニフェストに書いた内容を大切にして新人研修を行っているなと改めて感じられて*3、「及部さんの言語力すげー」となってました。

おわりに

今回は登壇者3人が共通して大事に思っていることをもとに新人研修マニフェストを作成してみました。

もしも、「こんなことも大事なんじゃね?」とか意見がありましたら、フィードバックくださいませ!

追記

既に頂いてたコメントに対しての私の考えを書いておきます。

調べ方を教えるのも重要なんじゃない?

それも含めて「リアルな現場体験」の中に包含するかもしれないです。

いくら特定分野の調べ方を教えたところで、別分野では調べ方が分からなければあまり意味がないかもしれません。「○○は社内用語だよ」と伝えてもキリがないです。

それよりかは、リアルな現場を提供した上で、それを調べるという体験がやはり重要かなと考えています。そこで、うまく調べられなかったという経験も大事かなと思っています。「こうやって調べれば良いんだよ」という、予めの「計画に従うこと」を目指していない気がします。

テストとか品質に対してこのマニフェストは難しいんじゃない?

はい、難しいと思います。kawagutiさんが似たようなことを仰ってた。

なので、「教える」となることが多い中でも、どのようにすればマニフェストの内容に近づけるのかを考えるのが腕の見せ所ですね。

*1:今回のDevelopers Summitでは、私個人の公募セッションもありましたが、そちらについては後日記事を書く予定です。

event.shoeisha.jp

*2:なんか、私がブログ記事でこの新人研修マニフェストを公開しちゃってますが、©︎TAKAKING22として良いと思うぐらいです

*3:実際、聴講者もそのように感じていたようです。

『Software Design 2024年2月号』に寄稿したテストの考え方を用いた、具体的なテストの改善例

はじめに

先日、私が第1特集「新しいソフトウェアテスト講座」の第1章「ソフトウェアテストとは何か?」を寄稿した、『Software Design 2024年2月号』が発売されました。

gihyo.jp

この雑誌を読んだ黒柴さんが、雑誌の第1特集をキッカケとして*1ブログ記事を書いてくださりました。ありがとうございます!

note.com

そこで、本記事では、上記の記事に載っている題材について、第1特集の第1章でお伝えした内容を活用して、改善案を書いてみたいと思います。

目次

黒柴さんのブログ記事の概要

参考元にしている黒柴さんのブログ記事では、以下のような題材を扱っていました。

仕様:
ある工場では、3直で24時間勤務を行っており、アプリの画面には現在の直で実施予定の作業情報を表示する
そのため、テスト対象の処理ルーチンでは、現在時刻を元に現在の直の開始時刻を算出する仕様としている
各直の勤務時間は以下のとおりとする

直ごとの勤務時間
テストケース:
担当者(実装、およびユニットテストを担当)が考えたテストケースは、以下のようなものだった
誤ったテストケース
このテストケースは、直の仕様を以下のようなパーティション分割であると整理して作成したものと予想される
テストケースの元にした直のパーティション
不具合の詳細:
上記のテストケースでは、日付が考慮されていない
そのため、項番4の7:29では前日の23:00を直の開始時刻とするべきところを、時刻のみを計算して直の開始日時としているため、当日の23:00を開始時刻として出力していた
この点では、対象の処理ルーチンが「直の開始日時」ではなく、「直の開始時刻」を算出していることに問題がある

提示された仕様では、直の情報に日付が定義されていないが、時刻のみならず日付も考慮したうえで、以下のように整理されるべきであると考える

日付を考慮した直のパーティション

テストプロセスを踏まえて考え直す

Software Design 2024年2月号』の私の寄稿の中で示している通り、個人的にはテストプロセスを踏まえた方が良いと思っています*2

ISTQBテスト技術者資格制度 Foundation Level シラバス 日本語版 Version 2023V4.0.J01(以下、JSTQB)で示しているテストプロセス

なので、まずはテスト分析から考えてみます。

テスト分析

今回の場合、「何をテストしたいのか」という考え(これを「テスト条件」と言います)でいくと、以下のような部分が気になります。

  • a)現在の勤務に応じて表示が変わるのか
    • 例えば、現在時刻が二勤の時間帯の場合は、"15:30"という表示になるのか
  • b)勤務が被っている部分についてはどのように表示されるのか
    • 例えば、現在時刻が15:31の場合は、一勤の時間帯なのか、それとも二勤の時間帯なのか
  • c)日付を跨いだ場合、適切に表示されるのか
    • 例えば、現在時刻が02:00の場合は、"00:00"とはならずに、"23:30"という表示になるのか
  • d)休憩時間中の場合、適切に表示されるのか
    • 例えば、現在時刻が12:00の場合は、"07:30"という表示になるのか

このうち、a)は元々行われていたテスト条件、c)は発生した不具合で漏れていたテスト条件となります。また、b)とd)は私が必要だと感じたテスト条件です。個人的には特にb)に不具合が潜んでいそうだなと感じています。

続いて、これらのテスト条件に対してのテスト設計技法の適用を考えます。

今回は時刻について、a)とb)についての同値分割および境界値分析と、c)についての同値分割および境界値分析と、d)についての同値分割および境界値分析を行うのが適切だと考えました。なぜ、a)b)とc)とd)で分けたかというと、一緒くたに表現すると、何をテストしようとしているのか見通しが悪くなると感じたからです。

現在の勤務に応じた表示に関するテスト設計およびテスト実装

テスト設計(同値分割)

以下のように同値分割します。

現在の勤務についての同値分割をした結果

同値分割で大事なのは、複数のパーテーションに属することがないようにすることです。

JSTQBシラバスでも以下のように書かれています。

パーティションは、重複してはならず、空でない集合でなければならない。

そのため以下のような表現は、同値分割法で表現できていないことになります*3

同値分割の考え方にそぐわない表現方法

テスト設計(境界値分析)

同値分割を行った結果を用いて、境界値分析を考えると、以下のようになります。

現在の勤務についての境界値分析をした結果(同値分割の※1周辺のみを抜粋)

今回の図では※1周辺のみを示しましたが、※2、※3周辺も同様に表現できるはずです。

テスト実装

テスト設計で明らかになった境界値を考慮して作成したテストケースは以下のようになります。

境界値を考慮して作ったテストケース

このうち、「???」とした部分は、元々の記載内容だけでは判断できなかった部分です。この部分こそ、チームで議論し、明らかにすべき箇所です。

このように、プログラムを1文字も書かなくてもテストを考えることができますし、場合によってはテスト実行をせずとも、テスト実装以前の段階で不具合を発見することができます

2024/02/06追記

元々の記載内容だけでは判断できなかった部分について、黒柴さんに補足していただきました。ありがとうございます!

日付を跨いだ場合の表示に関するテスト設計およびテスト実装

テスト設計

前述までと同様、同値分割を行います。なお、勤務が被っている部分については、前述のテスト設計を元に議論した結果、後続の勤務グループの時間として判断したとします。

日付を考慮して同値分割をした結果

境界値分析の図示は、前述と同様の表現となるため割愛します。

テスト実装

テスト設計で明らかになった境界値を考慮して作成したテストケースは以下のようになります。

境界値を考慮して作ったテストケース

ここまでと同様に、休憩時間中の表示に関するテスト設計およびテスト実装も行うことができるはずです。今回は説明を割愛します。

テストコードの表現への活用

さて、ここまで整理してきたテスト設計やテスト実装の情報は、テストコードに活かすことができないのでしょうか。

私は活かすことができると考えます。テストメソッド名やテストコードの構造化に役立てることができます。

元記事に書いてあったテストケースの表は以下のようになります。

テストケースの表

これをそのままテストコードで表現してしまうと、見通しが少し悪い(保守性が悪い)テストコードができてしまう可能性があります。少なくとも、数年後にこのテストケースを見た時に、どんな目的で書かれたテストなのかが分からなくなっているでしょう。

一方、今回のテスト分析で示したように、a)、b)、c)、d)で分けて表現するといかがでしょう。例えば、以下のようになります*4

public class DisplayDateTimeTest {

    @Nested
    @DisplayName("現在の勤務グループに応じた表示のテスト")
    class JudgeGroupTest {

        @Test
        @DisplayName("一勤と判断される時間帯のテスト")
        void judge_first_group(){
            assertEquals(displayDateTime("2024/02/05 13:00"), "2024/02/05 07:30");
        }

        @Test
        @DisplayName("二勤と判断される時間帯のテスト")
        void judge_second_group(){
            assertEquals(displayDateTime("2024/02/05 17:00"), "2024/02/05 15:30");
        }

        @Test
        @DisplayName("三勤と判断される時間帯のテスト")
        void judge_third_group(){
            assertEquals(displayDateTime("2024/02/05 23:45"), "2024/02/05 23:30");
        }
    }

    @Nested
    @DisplayName("勤務が複数被っている付近の時間帯のテスト")
    class DuplicateGroupTest {

        @Test
        @DisplayName("一勤の勤務時間と判断される最後の時刻のテスト")
        void judge_first_group_end_boundary_time(){
            assertEquals(displayDateTime("2024/02/05 15:29"), "2024/02/05 07:30");
        }

        @Test
        @DisplayName("二勤の勤務時間と判断される最初の時刻のテスト")
        void judge_second_group_start_boundary_time(){
            assertEquals(displayDateTime("2024/02/05 15:30"), "2024/02/05 15:30");
        }

        @Test
        @DisplayName("二勤の勤務時間と判断される最後の時刻のテスト")
        void judge_second_group_end_boundary_time(){
            assertEquals(displayDateTime("2024/02/05 23:29"), "2024/02/05 15:30");
        }

        @Test
        @DisplayName("三勤の勤務時間と判断される最初の時刻のテスト")
        void judge_third_group_start_boundary_time(){
            assertEquals(displayDateTime("2024/02/05 23:30"), "2024/02/05 23:30");
        }

        @Test
        @DisplayName("三勤の勤務時間と判断される最後の時刻のテスト")
        void judge_third_group_end_boundary_time(){
            assertEquals(displayDateTime("2024/02/06 07:29"), "2024/02/05 23:30");
        }

        @Test
        @DisplayName("一勤の勤務時間と判断される最初の時刻のテスト")
        void judge_first_group_start_boundary_time(){
            assertEquals(displayDateTime("2024/02/06 07:30"), "2024/02/05 23:30");
        }
    }

    @Nested
    @DisplayName("日付を跨いだ場合のテスト")
    class AcrossDaysTest {

        @Test
        @DisplayName("当日の三勤の勤務時間と判断される最後の時刻のテスト")
        void judge_today_third_group_end_boundary_time(){
            assertEquals(displayDateTime("2024/02/05 23:59"), "2024/02/05 23:30");
        }

        @Test
        @DisplayName("前日の三勤の勤務時間と判断される最初の時刻のテスト")
        void judge_yestarday_third_group_start_boundary_time(){
            assertEquals(displayDateTime("2024/02/06 00:00"), "2024/02/05 23:30");
        }
    }
}

テストコードのさらなる改善

上記のコードだと「どの勤務グループなのか」と「勤務グループの開始時刻はいつなのか」の2つの確認を合わせて行っています。そこで、さらにテストコードを改善してみます。

public class DisplayDateTimeTest {

    @Nested
    @DisplayName("現在の勤務グループの判定テスト")
    class JudgeGroupTest {

        @Test
        @DisplayName("一勤と判断される時間帯のテスト")
        void judge_first_group(){
            assertEquals(judgeGroup("2024/02/05 13:00"), "一勤");
        }

        @Test
        @DisplayName("二勤と判断される時間帯のテスト")
        void judge_second_group(){
            assertEquals(judgeGroup("2024/02/05 17:00"), "二勤");
        }

        @Test
        @DisplayName("三勤と判断される時間帯のテスト")
        void judge_third_group(){
            assertEquals(judgeGroup("2024/02/05 23:45"), "三勤");
        }
    }

    @Nested
    @DisplayName("勤務が複数被っている付近の時間帯の勤務グループの判定テスト")
    class DuplicateGroupTest {

        @Test
        @DisplayName("一勤の勤務時間と判断される最後の時刻のテスト")
        void judge_first_group_end_boundary_time(){
            assertEquals(judgeGroup("2024/02/05 15:29"), "一勤");
        }

        @Test
        @DisplayName("二勤の勤務時間と判断される最初の時刻のテスト")
        void judge_second_group_start_boundary_time(){
            assertEquals(judgeGroup("2024/02/05 15:30"), "二勤");
        }

        @Test
        @DisplayName("二勤の勤務時間と判断される最後の時刻のテスト")
        void judge_second_group_end_boundary_time(){
            assertEquals(judgeGroup("2024/02/05 23:29"), "二勤");
        }

        @Test
        @DisplayName("三勤の勤務時間と判断される最初の時刻のテスト")
        void judge_third_group_start_boundary_time(){
            assertEquals(judgeGroup("2024/02/05 23:30"), "三勤");
        }

        @Test
        @DisplayName("三勤の勤務時間と判断される最後の時刻のテスト")
        void judge_third_group_end_boundary_time(){
            assertEquals(judgeGroup("2024/02/06 07:29"), "三勤");
        }

        @Test
        @DisplayName("一勤の勤務時間と判断される最初の時刻のテスト")
        void judge_first_group_start_boundary_time(){
            assertEquals(judgeGroup("2024/02/06 07:30"), "一勤");
        }
    }

    @Nested
    @DisplayName("日付を跨いだ場合のテスト")
    class AcrossDaysTest {

        @Test
        @DisplayName("当日の三勤の勤務時間と判断される最後の時刻のテスト")
        void judge_today_third_group_end_boundary_time(){
            assertEquals(displayDateTime("2024/02/05 23:59"), "2024/02/05 23:30");
        }

        @Test
        @DisplayName("前日の三勤の勤務時間と判断される最初の時刻のテスト")
        void judge_yestarday_third_group_start_boundary_time(){
            assertEquals(displayDateTime("2024/02/06 00:00"), "2024/02/05 23:30");
        }
    }
}

このようにすることで、もしもテストがNGになった場合にも、どんなテスト条件に関わる部分(何についてのテスト)なのかわかりやすい形になり、保守性が上がります。

また、例えば「二勤の開始時刻が1530→15:00」と変更された(仕様が変わった)としても、変えるべきテストは「一勤の勤務時間と判断される最後の時刻のテスト」と「二勤の勤務時間と判断される最初の時刻のテスト」だと、テストメソッド名だけを見て予想を立てることができます

このように、テストコードの作成前に(特にテスト分析とテスト設計で)行うべきテストを整理することで、保守性の高いテストコードを書くことができます

おわりに

今回は、黒柴さんの記事を元に、テストプロセスのテスト分析〜テスト設計〜テスト実装、そしてテストコードの改善について考えてみました。

本記事では1つのケースを取り上げてみましたが、考え方については『Software Design 2024年2月号』に寄稿という形で書きましたので、よければそちらもご覧ください。

gihyo.jp

*1:

*2:JSTQBの中では「テスト計画」「テストのモニタリングとコントロール」「テスト完了」もありますが、今回は単一のテストについてなので割愛します

*3:同値分割法で表現できていないだけであって、仕様を理解する図としてはあっても良いと思っています

*4:テストコードに残すにあたって、同値分割のみにしたり、重複している境界値のテストケースは削ったりといった小さな工夫をしていますが、今回は説明を割愛します

Regional Scrum Gathering Tokyo 2024で今一番伝えたいことを発表してきました #RSGT2024

1月10日〜12日に開催されたRegional Scrum Gathering Tokyo 2024で登壇をしました。

Regional Scrum Gathering Tokyo 2024 - できるだけ大きなアウトカムが得られるように、シフトレフトとシフトライトの両面から製品開発に取り組んだお話 | ConfEngine - Conference Platform

RSGTで登壇したのは2021年以来だったので、3年ぶりの登壇になります。

軽くふりかえりをしてみます。(が、後半はポエムになってしまった…)

発表資料

speakerdeck.com

前回よりも嬉しかったこと「フィードバックの多さ」

前回と大きく違ったのは、発表後、多くの方に質問や感想を伝えてもらったことでした。

これは前回とコンテンツの内容が違っていたのが要因なのか、参加者がよりギャザリングを意識しているからなのかは分かりません。

しかし、個人的にはフィードバックが多い方が嬉しいので、今回のような反応の方が嬉しいです。

次回に向けての改善点「セッションの惹きつけ方」

今回は、よりAgileやDevOpsを意識したセッションタイトルにしてみました。また、私としてはQAやテスターに限った話をしているつもりは無かったので、セッションタイトルに「テスト」という単語を敢えて入れませんでした。

しかし、結果としてリアル聴講のうちQAやテスターと呼ばれる人の割合が多めのようでした。これは誤算でした。

(良い意味でも悪い意味でも)私には「テストをしている人」というラベルがあった方が、聴講者にとっては「聞いてみよう」と思ってくれるのかもしれません。また、「シフトレフト」や「シフトライト」といった単語がQA界隈以外には想定よりも浸透しておらず、そもそもセッションタイトルを見ただけでは聞く気になれなかったのかもしれません。

そういう意味では、来月のDevelopers Summitの公募で出したセッションタイトルの方が、より惹きつけられるタイトルなのかもしれません。

event.shoeisha.jp

待って!本当に改善点なの?

今回は確かにQAの聴講者が多めでした。ですが、それは改善すべきことなのでしょうか?

私の発表はきょんさんの発表いくおさんの発表ふりかえりの森さんの発表のように、聴衆を巻き込むタイプの発表ではありません。

RSGTやScrum系のイベントにおいて数少ないQAに光を当てたいという気持ちが強いです。万人に聞いてもらおうと努力した結果、QAらしさが失われてしまう発表になってしまったら元も子もないです。

自分が目指しているのは、「ライブでの聴講者数」ではなく「よりQAらしい発表」だと思ってます。本当は気にすべきではないアウトカムを目指して、測定しやすいが目標とは関係ないメトリクスを取って一喜一憂したくありません。

今回の発表のモチベーションは「まだ世になかなか出ていない『シフトライト』でのテスト活動の事例を話したい!」でした。ここの軸は見失いたくない。

しかもありがたいことに、RSGTは講演動画がYouTubeに上がります。つまり後から見返せる訳です。なので、発表時点で気付かれなくても良いのです。「あれ?以前に誰かがシフトライトについて話してたな?」と思った時に見返してもらえれば良いのです。

何はともあれお疲れ様でした

とここまで色々と書きましたが、これはイベントが終わっても悶々と考えていたので、記事にしてみた結果です。

RSGT2024は終わりました。ということは、RSGT2025の準備が始まります。

今度プロポーザルを出すのはRSGT2025かもしれませんし、また3年置いてRSGT2027ぐらいに出すかもしれません。ただ、その時にも「よりQAらしい発表」を目指すプロポーザルを書けるように日々精進したいと思います。

あ、そもそも今回の発表資料を公開しなきゃですね…。