SIer 的なテストに関するお話

先日 Scala 勉強会後の懇親会で開発テストについて話題になったので、補足と記録も兼ねてもう少しきちんと書き記しておこうと思う。

僕は金融系 SI - 主に保険と、まれに銀行システムのフレームワークや共通機能の設計開発、それに問題分析を行うエンジニアとして 10 年ほどやってきた。中でも一番長く携わったのが世界有数とも言える某巨大保険会社だった。SI に携わっていれば行く先々の現場でお客様の特性が異なるものだが、さすがにこのクラスともなると金額も要求も常識を逸している。他に赤い銀行、緑の銀行など、日本の大手金融企業特有の過剰とも言える品質要求と理想の追求で学んだテストに関する意識などをつらつらと書こうと思います。

そもそも SI って何ザンス?

本題に入る前に SI (System Integration) についてピンとこない人もいると思うので少し補足しておく。ただ慣例的な部分はお客様や SIer の企業文化によって様々だし、最近はオフショアで非開発体勢を取るも多くなっているので、あくまで僕が携わってきた現場ではという接頭辞を汲んで欲しい。

SI開発の事情

企業がシステム化を行う理由は、内務の効率化から新サービスの立ち上げまで様々だ。企業規模が大きくなるなるほどシステムがカバーする業務範囲は広くなり、それらの業務が相互に関係して複雑化する。もちろんすべての業務を把握した上で設計・開発・運用などのシステム知識も持っているようなスーパー人材などは存在しない (実際合ったこともない)。このため企業のシステム部は SIer と呼ばれるシステム開発を生業とする会社を使ってシステムの構築を行うわけです。

SI 開発での SIer の仕事は、一言で言えば「お客様 (=企業のシステム部門/プロパー) がシステムを開発するために技術面でのサポートを行う」事だ。プロジェクトの主体はあくまでお客様であって、システムの最終責任はお客様が負う契約を行う。役割分担としては、お客様が業務内容の取りまとめを担当し、SIer が実際に開発作業を行うといったところだろう。

発注するプロジェクトも億円単位の規模を超えれば、遅延や一部不履行といった開発リスクも跳ね上がってくるし、そのような開発を一次請けできる SIer も限られてくる。もちろんその SIer 内の人材だけでは何ともならないので (そもそも単価が高いし)、開発要件に適した人材を持つ協力会社を集めてプロジェクトの体制を整える。これは一見「丸投げ」に見えるかも知れないけど、僕はリソース管理や折衝事に費やす労力のハンパなさを目の当たりに見てきたので一概にそう思うのは間違いだと思っている (本当に丸投げしているだけの会社もよく聞くけどね)。

プロジェクトマネージャはプロジェクト成功のための全責任を請け負う代わりに、プロジェクトに関する金・人・時間すべてのリソースへの強力な裁量を与えられている。マネージャが所属する SI 企業はプロジェクト成功のために必要なリソース (主に人材とハード、製品知識) を確保する。協力会社はそれぞれ得意な専業分野を持っており、それを生かして立ちまわる。プロジェクトとしてそんな体勢が立てられます。

フェーズと開発フロー

さて、実際にプロジェクトが始まるといくつかのフェーズに分けて開発が進んで行きます。どのフェーズで何を行うかに関しては親の SIer や規模、状況、契約形態によって大きく違ってくるだろうが、僕が関わったプロジェクトの多くは大まかにこんな具合だった。

フェーズ 作業内容
要件定義 業務のヒアリングをして開発対象や作業範囲を決定。開発契約のための作業見積もり。
外部設計 要件から機能へ落とし込み。開発範囲の作業分割。画面やDB、帳票レベルのインターフェース定義。
内部設計 機能から実装へ落とし込み。個別の機能ごとに詳細な入出力や処理フロー定義。
コーディング 内部設計書に従ってひたすらコードを書く。モンキーな動作確認は行うが本テストはまだ。
単体テスト 内部設計書通りの実装になっていることを確認するためのテスト。開発端末ローカルで実行する。
結合テストA サービス単位でのモジュール結合テスト。テスト環境で一部モック化されていることも。
結合テストB 開発対象全体でのサービス結合テスト。テスト環境だが必要なミドルウェア等はほぼ全て揃っている前提。
システムテスト 本番環境でサブシステム(ホストやDWH等)との結合テスト。パフォーマンステストもここに含まれる。

こうやって並べると完全なウォーターフォールに見えるが、実際はプロトタイピングや検証、仕様矛盾の指摘、要件調整と差し戻しなどの細かいイテレーション (スパイラルと呼んでいた) が裏で繰り返されている。ただ契約のためなるべく「工程表通り」に進むので見た目上はウォーターフォールとして遂行するのだ。実際のところ SI 開発の現場で差し戻しやイテレーションが完全に排除できるなどと信じている人はいなかったし、イテレーションを最小限に抑えつつ(現実)どうやって工程通りに進めるか(理想)という調整がマネージャーの力量だったと思っている。

開発の内情に関しては、ここを読んでる殆どが開発者であろう事から省略する。また暇があったら書くかも知れない。

保守のインパク

プロジェクトが完了して一仕事終えたと思うのは開発者だけだ。企業にとってそれはスタート地点であって、今後そのシステムを運用して開発費用を回収しなければならない。そしてその間もアプリケーションの改修は続いてゆく。この保守というフェーズでは実運用中に見つかったバグを直すというだけではなく、以下に示すような様々な要因でアプリケーションを改修する必要性が出てくる。

業務形態は変化します

昨今のグローバル化や過競争で業務の流動性は高くなる一方。業務フローが変わればシステムもそれに合わせて変化する要望が上がってきます。あまり大きな変更は「新システム」として別に立てられますが、組織体系や業務、商品の整理統合、海外店舗進出や業務効率化、企業間の業務提携など、システムは様々な事情にあわせて改修が入ります。

これに関しては業務システム固有の話ではなく当然のことですね。

保守契約というものがありまして

家電製品でも同じだが、ソフトウェアやハードウェアの保守をメーカーが永遠に担保するという事は余程のプレミアムサービスでもない限りありえない。保守契約が切れても使い続けりゃ良いじゃん? って、それではリスクが高すぎて保守・運用を受け追ってくれる外注はいないだろう。知ってて使い続けてトラブルを起こしたのなら当然責任問題にもなるし、社会的な信頼性も大きく失うことになる。そもそもホストなどの汎用機契約はリースに近いのでハードごと撤収される。

保守契約に関してはシステムが運用上の不具合を全く持っていなくても、一定期間でサーバ、DB の入れ替えやバージョンアップを行う必要があるという話です。

法令が改訂されます

運用上の不具合がないのにもかかわらずアプリケーションを改修しなければならない別のケースに法令の改訂があります。食品安全法や消防法など、企業が扱う大抵の商品はそれに関わる何からの法令が存在しています。中でも法令の影響を大きくうけるのが契約と信用に価値を置く金融商品。例えば利息制限、契約期間、震災等の特例措置、その他様々なパラメータが時代に応じて変わるし、それに応じたアプリケーションの変更も必要になってきます。規制緩和などあれば新しい金融商品が生まれたり撤廃されたりといった変更も入ります。

まぁこのような様々な理由で開発が終わった後もシステムの改修が必要なんですね。

開発基準との戦い

僕は 1999 年頃から SI 開発に参加していたので Web 業務システム初期の開発基準策定に何度か立ち会う機会に恵まれた。これは今でもとても運が良かったと思っている。その中でもテスト基準の策定についてピックアップしてみようと思う。

プロジェクト発足当初から何らかのテストが必要であることはお客様も開発部隊も分かっていたが、どのフェーズでどのような作業を行い何を担保するかについては全く白紙状態だった。なにせ Web + オープンシステムベースの大規模開発は (少なくとも金融業界では) 日本初だったからだ。結局のところ、試行錯誤の末に回り始めたテストの内容と基準は以下のように落ち着いた。

他の人へ引き渡すための第一段階、単体テスト

  • ファンクション (メソッド) 単位で動作の正しさを検証する。
  • 着目点は「実装者の意図した動きとなっている事」「不要なコードが存在しない事」「テストされていないステップが存在しない事」。
  • 開発者はテストコード (ドライバ) を作成し開発端末で実施する。
  • テスト仕様書は作成せず、成果物はカバレッジ 100% のエビデンスのみ。ただし構文上書かなければいけないが実行不可能なステップについてはレビューの上 100% に達していないくても良い。
  • 開発が間に合っていない他者モジュールや、イレギュラー動作が必要なモジュールはモック (スタブ) を使用しても良い (このような組み合わせを検証するのは次の統合テストAで)。
  • 同様に実装者の仕様解釈の誤解も次フェーズで拾う。
  • 当初は分岐ごとにログを仕込んで (!?) その出力結果を集計していたが、後に dJUnit で自動化された。

機能的に結合できることを確認、統合テストA。

  • 着目点は「ファンクションの組み合わせがサービスとして機能すること」。
  • サービスとは「ユーザ管理」「商品管理」「受付」といったような業務の単位。基本的に全て結合する想定だが、開発が遅れている部分と連携する場合はモックで代用。
  • テスト仕様書は詳細設計に対するもので、検証対象は画面入出力、DB 入出力、サービス間インターフェース等。機能面のテストが主で、要件に対しする機能不備がないかは結合テストBで検証する。
  • 本番構成のサブセット (冗長化なし、1CPU、少メモリ等) となる検証環境で検証する。
  • 最初はテスト手順書を作成して手作業で、後に自動化ツールが導入された。

要件通りに機能することを確認、統合テストB。

  • 着目点は「サービス及びサブシステムを結合して機能すること」。
  • テスト仕様書は基本設計に対するもので、技術的な機能は完了している前提で業務要件通りの動作をしているかの検証。
  • 全てのサービス及びサブシステムを結合した状態でテストを行う。ただし結合テストAと同様に本番構成のサブセットとなる検証環境。

あと一息、システムテスト

  • 完全な本番環境に構成しテストを行う。ロードバランサから RAID、冗長構成まで完全セット。
  • 開発よりも運用系のテストが主体で、本番用のホストやサブシステムとの疎通確認。パフォーマンステストや問題分析もシステムテストに含まれる。
  • システム利用者となるお客様の各部署に使ってもうらのもこの構成から。

単体テストもエンヤコラ

実装者レベルで一番手間暇がかかるのが単体テストだろう。単体テストカバレッジについて、僕は当初「ホワイトボックステスト無しはやらなさすぎだが 100% もやりすぎだ」という立場だった (Writing Solid Code の影響)。しかし自分が実装したコードについて実施してみると、いくつかのバグと共に処理上の疑問点や問題点も多く気づかせてくれた。例えば以下のようなものだ。

  • どうしても通らない条件文について調べる → そもそもその条件で使用される事がなかった → 確認してみると、そのケースで来たらバグなのでアサーションに変更。
  • コードと睨めっこして他にケースは無い事を確認しているとき、ふと else ケースが存在していることに気づいた。

動作や仕様以外にも、自分の書いたコードを今度は使う側になってガリガリと使い倒してみたり、レビューワーに視点を変えて見なおしてみることは、微妙な使いづらさや分かり難さに気づくきっかけになった。この点に関してコードレビューやペアプロに準ずる効果ながら他人の時間を浪費しないという点でとても有用だったと思っている。誰でも指摘されるより自分で気づいた事のほうが経験に残るしね。残るといえば成果物として今後も使える自動テストコードが残るという点は毎度人手暇のかかるレビューやペアプロより優秀だ。

僕が手がけた開発部隊向けのアプリケーションデザインガイドでは想定外のケースについて「バグ」とみなし一貫して例外としている。バグ的状況は、自分のモジュールに直接的な影響はなくても早い時点で弾かないとより後工程の不可解な挙動で無駄な作業工数が消えることになるからだ。まぁテストで気づけばまだマシだが、もし本番にリリースされてしまってデータ破壊が発生したら最悪の状況だろう。業務上想定し得るイレギュラーケース以上のバグ的状況について一切考慮しない方針または実装をたまに見かけるが、明らかにその判断は間違いだ。

修正の入ったコードは単体テストからやり直しとなる (と同時にいくつかの仕様書の修正も必須だった)。つまり実コードとテストコードは必ずセットで存在していた。モンキーテスト (アドホックテスト) は個々の開発者が必要に応じて行うものであって、テストクリアの基準に含まれない。もっとはっきり言えば、それらは単なる動作確認であってテストではない。

統合テスト以降については長くなるのでここでは省略する。また暇があれば書くかも知れない。

テストの必要性

テストの目的は何かと問われれば答えは一つ。「システム品質の担保」だ (細かい事だが確保でなく担保である事が重要)。これだけ聞くとリリースまでに品質基準をクリアする事かと思うかもしれないがそうではない。保守運用のフェーズまで考慮した品質担保の方法を残せなければプロジェクトとして次第点に満たないし、お客様の要望は当然そこだろう。

保守運用フェーズというのはシステムを開発したメンバーがもうそこには居ない状況となる。しかしアプリケーションの改修要望は上がくるし、データベースのバージョンアップや OS のバグフィクスなどの作業も入ってくる。このような状況を想定して考慮しておかなければならないのが前機能保証となる。

前機能保証

前機能保証とは、システム改修の前と後で、改修された部分が正しく反映されており、それ以外の部分が影響を受けていないことを保証することだ。ぶっちゃけ言うと改修前に行ったテストの結果と改修後の結果が等しくなる事を検証する。注意しなければならないのは実装した人間が既に居ないため、ケースのを一つ一つ手作業で実施するのが現実的でないという事。

ではどうするか?

あらゆるテストはできうる限り限り自動化されなければならない。テストの自動化は開発よりむしろ保守運用フェーズのためにあると言って良いだろう。何故ならその自動テストがシステムリリース後に何度も何度も繰り返し実行されるからだ。開発で跳ねたテスト自動化のコストは保守運用で取り戻すという前提に立たなければならない。開発が終わったらテストコードは無駄になると思っている、もしくは本当に捨てているようなプロジェクトは考えを改める必要がある。

前機能保証のできないシステムがどうなるか想像するのは容易だろう。何かにつけてシステム変更時の影響が測れなくなるという状況となる。OS やミドルウェアのバージョンアップによる影響が測れなくて「こんなセキュリティホールのあるバージョンいつまで使ってんの?」と、アプリケーション実装はリファクタリングや設計変更による影響が測れなくて、既存コードに手を入れられず拡張拡張で保守性が悪く見難く肥大化したしたモジュールで埋め尽くされるだろう。今まで見てきた感覚では概ね半年〜2年程度であちこちが腐りはじめる。

この前機能保証に関しては開発と言うよりお客様側が危機感を持たなければならない部分であるが、多くは保守運用までの長いスコープを見据えた上で自動テストにコストを割こうと判断できるには至っていないのが残念なところ。ただ金融系 SI に関しては毎年の法令改正でアプリケーションの改修が入るという前提があるので、テストの自動化と保守への引継ぎについては相当な昔から習慣的に行われてきた。

金融系システムの障害は時に巨大な社会的被害をももたらす。五十日(ごとうび)に振込が止まれば何社もの零細企業が吹っ飛ぶし、日経平均株価にも波及するほど大きくなる。さらに生命保険の商品は死ぬまで、70 年前に販売した保険商品のメンテナンスを未だに続けなければならないのである (ここらへんの事情を知らず「金融系開発は過剰な品質追求だ」と一概に言うのはタダの素人だと思う)

強引なまとめ

文章を書くのに少々時間をかけすぎてしまったので、書こうと思っていた残り部分とまとめを箇条書きにしておきます。

  • コードレビューは開発品質向上のために効果がある (ただしレビューワーという属人性を持ってしまう以上、それは定量的な品質を担保するテストの置き換えにはならない)。
  • 「このケースはめったに無い」とは、逆に言えば何時か必ず起きる事だ。その動作を考慮しないという人は技術者として論外。
  • 開発において「正しく動く」とはテストをクリアしたかどうかであって、モンキーテストはテストの範疇に入らない。
  • カバレッジ 100% のホワイトボックステストもスキル向上のために効果があるよ。
  • テストは出来る限り自動化しなければならない。自動化したテスト込みで引き継げて初めてマトモに保守運用が回る。
  • エスプレッソは砂糖を入れるものだ。

僕が経験してきた SI 開発は他の現場と比べて極端なケースが多かったと感じています。ただ金融系開発は確かに過剰とも言える作業が多いんですが、ネット上では手抜きを正当化するために金融開発を引き合いに出して揶揄しているようなケースもまま見られるのが残念なところです。幸い最近は Web 開発や携帯電話/スマートフォン開発も「やっぱテストって大事だよねー」という風潮に熟成してきているように見受けられます。愚筆で書ききれていない部分も多くありますが、そんな人たちの参考に何か汲むべき所があるか… 後は任せた(笑)。