ユニットテストC ++:テスト対象


20

TL; DR

優れた有用なテストを書くのは難しく、C ++でのコストは高くなります。経験豊富な開発者は、何をいつテストするかについての論理的根拠を共有できますか?

長い話

私はチーム全体でテスト駆動開発を行っていましたが、実際にはうまくいきませんでした。多くのテストがありますが、実際のバグやリグレッションがあるケースをカバーすることはないようです-通常、ユニットが相互作用しているときに発生し、孤立した動作からではありません。

これは多くの場合、ユニットレベルでテストするのが非常に難しいため、TDDの実行を停止し(開発を実際にスピードアップするコンポーネントを除く)、代わりに統合テストの対象範囲を増やすためにより多くの時間を費やしました。小規模なユニットテストは実際のバグをキャッチすることはなく、基本的にメンテナンスオーバーヘッドにすぎませんでしたが、統合テストは本当に努力する価値がありました。

今、私は新しいプロジェクトを継承しましたが、それをテストする方法を知りたいと思っています。ネイティブのC ++ / OpenGLアプリケーションであるため、統合テストは実際にはオプションではありません。しかし、C ++でのユニットテストはJavaよりも少し難しく(明示的にものを作成する必要がありますvirtual)、プログラムはオブジェクト指向ではないため、いくつかのものをモック/スタブすることはできません。

テストを書くためにいくつかのテストを書くためだけに、すべてを引き裂いてオブジェクト指向化したいとは思いません。だから私はあなたに尋ねています:それは何のためにテストを書くべきですか?例えば:

  • 頻繁に変更する予定の関数/クラス
  • 手動でテストするのがより難しい関数/クラス?
  • すでにテストが簡単な関数/クラス?

敬意を表するいくつかのC ++コードベースを調べて、テストがどのように行われるかを確認し始めました。今はChromiumのソースコードを調べていますが、コードからテストの理論的根拠を抽出するのは難しいと感じています。人気のあるC ++ユーザー(委員会、本の著者、Google、Facebook、Microsoftなど)がこれにどのようにアプローチしているかについて、良い例や投稿がある場合は、さらに役立つでしょう。

更新

これを書いて以来、私はこのサイトとウェブを探索しました。良いものを見つけました:

悲しいことに、これらはすべてJava / C#中心です。Java / C#で多くのテストを作成することは大きな問題ではないため、通常は利益がコストを上回ります。

しかし、上で書いたように、C ++ではより困難です。特に、コードベースがそれほどオブジェクト指向でない場合は、ユニットテストのカバレッジを良好にするために、物事をひどく混乱させる必要があります。例えば、私が継承したアプリケーションにGraphicsは、OpenGLの上にある薄層の名前空間があります。すべてのエンティティをテストするには、すべてのエンティティがその機能を直接使用するため、これをインターフェイスとクラスに変換し、すべてのエンティティに挿入する必要があります。それはほんの一例です。

したがって、この質問に答えるとき、テストを書くためにかなり大きな投資をしなければならないことに注意してください。


3
C ++の単体テストの難しさに対して+1。単体テストでコードを変更する必要がある場合は、変更しないでください。
DPD

2
@DPD:確かではありませんが、何かテストする価値があるとしたらどうでしょうか?現在のコードベースでは、すべてがグラフィックス関数を直接呼び出すため、シミュレーションコードではほとんどテストできません。また、それらをモック/スタブすることはできません。今すぐテストできるのは、ユーティリティ関数だけです。しかし、コードを変更して「テスト可能」に感じるのは間違っています。TDDの支持者は、これにより、考えられるすべての方法ですべてのコードが改善されるとよく​​言いますが、謙虚に同意しません。すべてがインターフェースといくつかの実装を必要とするわけではありません。
-futlib

最近の例を挙げましょう。1つの関数(C ++ / CLIで記述)をテストするのに丸一日かかりましたが、このテストではテストツールMS Testが常にクラッシュしました。単純なCPP参照に問題があるようです。代わりに、呼び出し関数の出力をテストしただけで、うまく機能しました。UTの1つの機能に1日費やしました。それは貴重な時間の損失でした。また、自分のニーズに適したスタブツールを入手できませんでした。可能な限り手動でスタブを作成しました。
DPD

それは私が避けたいものの一種に過ぎません:DIは、C ++開発者がテストについて特に実用的でなければならないと推測します。あなたはそれをテストすることになったので、私はそれでいいと思います。
-futlib

@DPD:私はこれについてもう少し考えました、そしてあなたが正しいと思う、質問は私がどんな種類のトレードオフをしたいかです。いくつかのエンティティをテストするために、グラフィックシステム全体をリファクタリングする価値はありますか?私が知っているバグはそこになかったので、おそらく:いいえ。バグが発生し始めたら、テストを書きます。残念です。コメントだから答えを受け入れられません:)
futlib

回答:


5

さて、単体テストは一部にすぎません。統合テストは、チームの問題に役立ちます。統合テストは、ネイティブアプリケーションおよびOpenGLアプリケーションに対しても、あらゆる種類のアプリケーションに対して作成できます。Steve FreemannとNat Pryceによる「テストに導かれたオブジェクト指向ソフトウェアの成長」(http://www.amazon.com/Growing-Object-Oriented-Software-Guided-Signature/dp/0321503627など)を確認してください。GUIとネットワーク通信を備えたアプリケーションの開発をステップごとに案内します。

テスト駆動ではなかったソフトウェアのテストもまた別の話です。Michael Feathersの「レガシーコードを使用した効果的な作業」(http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052)を確認してください。


私は両方の本を知っています。事柄は次のとおりです。1. TDDがうまく機能しなかったため、TDDを使用したくない テストが必要ですが、宗教的には必要ありません。2. OpenGLアプリケーションの統合テストは何とかできると確信していますが、手間がかかりすぎます。研究プロジェクトを開始するのではなく、アプリの改善を続けたいと思います。
futlib

コードはテスト可能(再利用可能、保守可能など)に設計されていないため、「事後」のユニットテストは常に「最初のテスト」よりも難しいことに注意してください。とにかくやりたい場合は、TDDを避けてもMichael Feathersのトリック(縫い目など)に固執するようにしてください。たとえば、関数を拡張する場合は、「スプラウトメソッド」のようなものを試して、新しいメソッドをテスト可能にしてください。それは可能ですが、より難しい私見です。
EricSchaefer

非TDDコードはテスト可能に設計されていないことに同意しますが、それ自体は保守も再利用もできないとは言いません。上記でコメントしたように、いくつかのことはインターフェイスと複数の実装を必要としません。Mockitoではまったく問題ありませんが、C ++では、スタブ/モックしたいすべての関数を仮想にする必要があります。とにかく、今のところテストできないコードが私の最大の問題です。いくつかのパーツをテスト可能にするためにいくつかの非常に基本的なことを変更する必要があります。
futlib

もちろん、私が書いた新しいコードをテスト可能にするように注意します。ただし、このコードベースでの現在の動作方法では、簡単ではありません。
futlib

機能/機能を追加するときは、テストする方法を考えてください。dependenciesい依存関係を挿入できますか?この関数が行うべきことを関数が行うことをどのように知っていますか?動作を観察できますか?正確性を確認できる結果はありますか?チェックできる不変式はありますか?
EricSchaefer

2

それは恥ずべきTDDです「あなたのためにうまくいかなかった」。それがどこを向くかを理解する鍵だと思います。TDDがどのように機能しなかったのか、どのように改善できたのか、なぜ困難があったのかを再確認して理解してください。

そのため、もちろんユニットテストでは、発見したバグを検出できませんでした。それは一種のポイントです。:-)インターフェースがどのように動作し、適切にテストされていることを確認する方法を考えて、そもそもバグが発生するのを防いだため、これらのバグは見つかりませんでした。

答えるには、あなたが結論したように、テストするように設計されていないユニットテストコードは難しいです。既存のコードの場合、単体テスト環境ではなく、機能テスト環境または統合テスト環境を使用する方が効果的です。特定の領域に焦点を合わせてシステム全体をテストします。

もちろん、新しい開発はTDDの恩恵を受けます。新しい機能が追加されると、TDDのリファクタリングが新しい開発のテストに役立ち、一方でレガシー機能の新しいユニットテストの開発も可能になります。


4
私たちは約1年半TDDを行いました。それでも、TDDプロジェクトをTDDなしで(ただしテストなしではなく)行った以前のプロジェクトと比較しても、実際にはより安定しているとか、コードの設計が改善されているとは言いません。多分それは私たちのチームです:私たちは多くをペアリングしてレビューしますが、私たちのコード品質は常にかなり良いものです。
futlib

1
考えれば考えるほど、TDDはその特定のプロジェクトの技術であるFlex / Swizにうまく適合しなかったと思います。オブジェクト間の相互作用を複雑にし、単体テストをほとんど不可能にする多くのイベントとバインディングとインジェクションが進行中です。これらのオブジェクトを分離しても、そもそも正しく動作するため、改善されません。
futlib

2

C ++でTDDを行っていないのでコメントできませんが、コードの予想される動作をテストすることになっています。実装は変更できますが、動作は(通常?)同じままでなければなりません。Java \ C#中心の世界では、パブリックメソッドのみをテストし、期待される動作のテストを作成し、実装前にそれを実行することを意味します(通常、実行するよりも良い方法です:))。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.