TDD(テスト駆動開発)のメリットデメリット

前提

アジャイルもどき開発で満足なユニットテストが実施できていない人。検収期限ありきで、リソースのほとんどがコーディングに取られてしまうプロジェクトに所属している人。効率の良いコーディングをしたいと思っているが、方法論で躓いてしまっている人向け。

テスト駆動開発とは?

テストありきな開発手法を指す。まずはテストコードを書いてから、メソッドを追加していく形になる。

「この振る舞いの条件を満たすには、こういった戻り値が必要になる。だから、こんな入出力のはず」

で、以下のようなコードが出来上がる。
人を管理するクラスを最終的に目指したい。

    <TestMethod> Public Sub ユーザーの名前の入出力のテスト()

        'Personクラスはすでにあるものとする
        Dim person As New Person

        '名前をセット
        person.SetName("Banpeisya")

        '取得した名前の検証
        Assert.AreEqual("Banpeisya", person.GetName)

    End Sub

テスト駆動開発の作法に則ると、Personクラスはコンストラクタを含め、「メソッドが一つも存在しない状態」でよい。以下の状態で良いとされている。

Public Class Person
End Class

当然、そんなメソッド存在しないよって Visual Studio が怒るわけだが、そんなのは気にしない。とりあえず、必要そうなメソッドを書き出していく。めんどくさかったら、メソッド名も日本語でいい。

person.名前を登録()

後ほど、適した名前に変えてやればいいだけのことだ。
※テストのメソッド名は、エンドユーザーやユーザーが実行できる個所ではないので、わかりやすく日本語で書くのが一般的。

person.名前を登録(“番兵舎”) とした場合、赤波線で警告が出ていると思う。そこで補完コードを選択するなり、Alt+Enterするなりして、Visual Studio 任せでコードを自動生成してもらう。

Public Class Person
    Public Sub SetName(v As String)
        Throw New NotImplementedException()
    End Sub

    Public Function GetName() As String
        Throw New NotImplementedException()
    End Function
End Class

これで実行してみると、ロジックが導入されていないので、当然エラーになる。

「ああ、なんか赤くなったな」

ここまでがテスト駆動開発における第一ステップとなる。
ただし、このエラー確認については、Microsoftのチュートリアルを見ても、海外のTDDの開発手法を見ても、存在しないステップのようだ。まことしやかに「必ずやらなければならない」とされている風潮には疑念が残るものの、とりあえず長いものに巻かれる必要がある(※1)ので、こういうものだ、としておく。

※1 「○○さん、まじめにテストやっていない!」と怒られる。。

さてはともあれ、ここまで来たらあとは単純な話で、ロジックをさくっと埋めていく。

Private _name As String

Public Sub SetName(name As String)
    _name = name
End Sub

Public Function GetName()
    Return _name
End Function

プロパティでいいでしょ?? はい。確かにごもっとも。ただ、VBの場合、SetGetを書くのが少々メンドイ。setterをコンストラクタで行うとした場合、C#だと private set;get; と書けばよいが、VBだとちょっとした振る舞いのある行数になってしまう。public なプロパティにしてしまうと、隠蔽できなくなるという点もあるので、Personとういクラスの意味を考えると「微妙(※2)」という結論になった。

※2 名前がない人はいないはずなので、コンストラクタでセットすべきじゃない?という結論に至るだろうなぁという予想があった。

これでテストを実行すると、グリーンになる。
テスト成功だ。ここまでが第二ステップ。

最後に第三ステップ。先にちょっと触れたが、Person「人」というクラスの意味合いで考えると、名前がない人が存在しうるシステムではない限り、絶対に必要な要素といえる。だったら、コンストラクタで名前をセットすればいいのでは?という指摘が入るはずだ。確かにそうだなぁとなって、コードをブラッシュアップしていく。これがリファクタリングという最終ステップになる。
結果的に下記となった。

Public Class Person

    Private _name As String

    Sub New(name As String)
        _name = name
    End Sub

    Public Function GetName()
        Return _name
    End Function

End Class

後付けで名前の変更ができないという縛りがあるものの、これで良しとする。
idなどの一意のものにしたほうが柔軟性が上がるので、サンプルとしては不出来でした。。

■メリットについて

この一連のコーディングだが、先駆者の方がよほど優秀だったんだろう、ほかの開発手法に比べてしっかりと根付いていると思う。アジャイルなんかと比べると、「おこがましい」レベルの差がある。少なくとも、まともにアジャイルができている現場は、一度も経験したことがない。

話をもどす。
開発手法がわかりやすく、しかもルールが浸透していることもあって、どの現場でもテスト駆動しているところは「大体同じような感じ」のようだ。これは、システム開発においては大きなアドバンテージになる。現場や言語が変わったら、同じ開発手法であっても千差万別、「ではない」からだ。つまり、経験しているかしていないかの差は大きいが、一度しっかりとやってしまえば、スキルとして計上できる。

あとはやはり、単純バグが発生しずらいスキームを作れることが大きいだろう。javaの台頭と合わせてオブジェクト指向が浸透してきたので、コピペソースは格段に減ったはずだが、いまだに手続き型コードを量産している現場は少なくない。
このコピペソースが単純バグを発生させる温床になる。連番の上書きミス、コピペソースの一部修正漏れなど、ある程度経験を積んだエンジニアであれば経験済みのはず。
テスト駆動開発にすると、この単純バグの発生率が極端に低下する。
※テストケースすらコピペしているのであれば、あきらめるほかない。。あきらめるほかないのだ。。。

品質が向上する、これもメリットとしては大きい。

■デメリットについて

テストコードを書く必要があるので、その分時間がかかる、と言われている。
これは、そのソフトウェアがどの程度のライフサイクルを見込んでいるかによって、大きく意見が分かれることだろう。検収印をもらって終わり、の使いきりのプロジェクトであれば、確かにテストコードを書く分の時間はロスタイムになってしまう。
瑕疵責任(※3,4)の度合いにもよるが、仮に半年とした場合、期間を乗り切ってしまいさえすればいいのだ。仕様書にあいまいさを持たせて(むろん、ばれないことが前提)おけば、瑕疵責任を追及されない(できない?)かもしれない。

※3 請負契約の場合、一定期間の品質担保義務が発生する。
※4 昨年(20年)に法改正があり、契約不適合(瑕疵)を「知った時」から1年以内に通知をすればよいという形になったようだ。

ただ、保守やアップデートもライフサイクルに入っているのであれば話は変わってくる。
テストコードがあり、リファクタリングも終わっているコードであれば、度合いこそあれ、ある程度のアップデートには耐えられる状態に仕上がっているはずだ。

まとめ

国民性なのかもしれないが、立場が上に行けば行くほど「変化」を嫌う。リーダーの知らない組み込みメソッドを呼び出しただけで、「修正して」と指示が来る現場があるという話も耳にする。いまだに配列を使用して、ジェネリックリストでLinqしたら「やめてもらえます?」なんて言われたことも、個人的には経験している。

テスト駆動開発は、今のところ損を感じたことがない開発手法だ。20年以上、システム開発に携わっている身としては、デメリットを感じたことがないというのはさすがに経験したことがない。大体、何らかの罠があって、はまって痛い目にあってきた。もしかしたら、未来で痛い目に合うかもしれないが、、、ことTDDにおいては「変化を嫌う人」であっても導入を検討を勧めたい。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です