アサーションはいつプロダクションコードに残すべきですか?[閉まっている]


166

comp.lang.c ++。moderatedで、C ++ではデフォルトでデバッグビルドにのみ存在するアサーションを本番用コードに保持する必要があるかどうかについての議論があります。

明らかに、各プロジェクトは一意であるため、ここでの私の質問は、アサーションを保持する必要があるかどうかについてそれほどではありませんが、その場合、これは推奨できる/良いアイデアではありません。

アサーションとは、次のことを意味します。

  • falseの場合にソフトウェアのバグを明らかにする条件をテストするランタイムチェック。
  • プログラムを停止するメカニズム(多分本当に最小限のクリーンアップ作業の後)。

私は必ずしもCやC ++について話しているわけではありません。

私自身の意見では、プログラマーであるがデータを所有していない場合(ほとんどの商用デスクトップアプリケーションの場合)、失敗したアサーションはバグを示しているので、データを保持する必要があります。バグが発生すると、ユーザーのデータが破損するおそれがあります。これにより、出荷前に強力なテストが必要になり、バグがより見やすくなり、発見および修正が容易になります。

あなたの意見や経験は何ですか?

乾杯、

カール

ここで関連する質問を参照してください


応答と更新

グラハムさん、

アサーションはエラーであり、純粋で単純であるため、アサーションは同じように処理する必要があります。エラーはリリースモードで処理する必要があるため、アサーションは実際には必要ありません。

そのため、私は主張について話すときに「バグ」という言葉を好む。それは物事をより明確にします。私にとって、「エラー」という言葉は曖昧すぎます。欠落しているファイルはエラーではなくバグであり、プログラムはそれを処理する必要があります。nullポインタを逆参照しようとするのはバグであり、プログラムは何かが悪いチーズのようなにおいがすることを認めるべきです。

したがって、ポインタをアサーションでテストする必要がありますが、ファイルの存在は通常のエラー処理コードでテストします。


少し話題外ですが、議論の重要なポイントです。

ヘッドアップとして、失敗したときにアサーションがデバッガーに侵入した場合は、なぜそうしないのか。しかし、完全にコードの制御外にあるファイルが存在できなかった理由はたくさんあります:読み取り/書き込み権限、ディスクがいっぱい、USBデバイスが抜かれているなど。あなたはそれを制御できないため、アサーションはこれに対処する正しい方法ではありません。

カール


トーマス、

はい、私はコードコンプリートを持っています。その特定のアドバイスに強く同意しないと言わなければなりません。

カスタムメモリアロケータが失敗し、他のオブジェクトがまだ使用しているメモリのチャンクをゼロにするとします。私はたまたま、このオブジェクトが定期的に逆参照するポインターをゼロにします。不変条件の1つは、このポインターがnullになることはないということです。ポインタが突然nullになった場合はどうしますか。あなたはちょうどそれの周りにif()、それがうまくいくことを望んでいますか?

ここでは製品コードについて話しているので、デバッガーに侵入したり、ローカル状態を検査したりする必要はありません。これはユーザーのマシンの実際のバグです。

カール


3
ソフトウェアエンジニアリングSEには興味深い関連記事があります(ただし、この議論はC ++に焦点を当てています):リリースビルドにアサーションがあるはずです
ソニー、2018

回答:


84

アサーションは、古くならないコメントです。それらは、どの理論状態が意図されているか、およびどの状態が発生してはならないかを文書化しています。コードが変更されて状態が変更を許可された場合、開発者はすぐに通知を受け、アサーションを更新する必要があります。


16
@jkschneider:ユニットテストは、プログラムのコードのスコープ内で物事をテストするためのものです。アサーションは、コードがそれらの仮定の下で処理を続行する前に、コードが基づく仮定が実際に実際に真であることを保証するためのものです。それらが「ドキュメンテーション」であるのは、そうでないことが判明した場合、プログラムは中止され、仮定を述べ、仮定が成立しなかったことを示します。もちろん、アサーションをコード内のドキュメントとして読むこともできます。

2
アサートはコメントに関連しているため、これは最良の回答です。コメントについて考えるのに役立つ方法です。開発中は常にマシンテストが行​​われているため、コメントからのステップアップですが、最初に人間の読者にとって常に意味のあるものでなければなりません。コメントのように、それらはロジックまたは最終実行の一部であってはなりません。コメントと同じように、言語がコンパイルされているか解釈されているか、ロールアウト計画、難読化戦略などに応じて、コメントを残したり削除したりできます。コメントが実際にバグを引き起こした例を1つ見ましたが、奇妙なもの。
DaveWalley、2014

1
具体的には、アサートは情報を提供するものであり、機能するものではありません。アサート自体は、プログラムのフローや結果には影響しません。一方、例外はプログラムのフローを変更し、結果として発生します。
ヨーヨー

59

Steve McConnellのCode Completeを引用させてください。アサーションに関するセクションは8.2です。

通常、プロダクションコードでアサーションメッセージをユーザーに表示しないようにします。アサーションは、主に開発およびメンテナンス中に使用するためのものです。アサーションは通常、開発時にコードにコンパイルされ、本番用にコードからコンパイルされます。

ただし、同じセクションの後半で、このアドバイスが提供されます。

非常に堅牢なコードの場合は、とにかくアサートしてからエラーを処理します。

パフォーマンスが問題にならない限り、アサーションはそのままにして、メッセージを表示するのではなく、ログファイルに書き込むようにしてください。アドバイスもコードコンプリートにあると思いますが、今のところ見つかりません。


7
Code Completeの2番目の引用が意味することは、アサーション(本番用コードでコンパイルされる)が必要であり、「if(!condition){AttemptGracefulRecovery();}」必要であることです。つまり、donプログラムの不変条件の違反がプログラムをクラッシュさせないようにしてください。
ヨーヨー

34

プログラムをオフにした方がプログラムの実行が大幅に速くなることが測定されていない限り、製品コードではアサーションをオンのままにします。

より効率的であることを証明するために測定する価値がない場合、パフォーマンスギャンブルのために明快さを犠牲にする価値はありません。」-Steve McConnell 1993

http://c2.com/cgi/wiki?ShipWithAssertionsOn


11
実際、発生する最悪の事態は、アサートではない何かが原因でコードがクラッシュした場合です。コードが間違いなく後で100%の確率でクラッシュする場合、アサートは絶対に存在する必要があります。ポインタを逆参照する場合、それが以前にnullではないことを暗黙的にアサートする必要があります。数値で除算すると、それはゼロではないと断言します。アサートを削除すると、すべてのクラッシュの場所が文書化されなくなります。実際の問題は、サブシステムがクラッシュしてウォッチドッグによって再起動されるようにプログラムを構成することではありません。
Rob

5
assert ref != null;異なっているif (ref == null) throw new IllegalArgumentException();あなたが偽の可能性が前提条件のために最初に使用してはいけません。あなたは偽るassertことができないもののために使用する必要があります。例、int i = -1 * someNumber; i = i * i;そして後にiポジティブであることを人々に思い出させるためにassert i > 0;
キャプテンマン

1
「実際には、発生する最悪の事態は、アサートではない何かが原因でコードがクラッシュした場合です。100%の確率でコードが後で確実にクラッシュする場合、アサートは絶対に存在する必要があります。」-これは誤った二分法です。
Rob Grant、

2
@RobertGrant:多くのプログラムにとって、クラッシュは起こり得る最悪の事態からはほど遠いものです。建物や橋の設計の健全性をチェックすることになっているプログラムの場合、設計が健全であると誤って報告することは、それができる最悪のことである可能性があります。外部に公開されているが機密データへの読み取り専用アクセス権があるプログラムの場合、そのデータの漏えいは、プログラムが実行できる他の何よりも悪い場合があります。原因を示す意味のないクラッシュは「起こり得る最悪の事態」であるという考えは、はるかに悪い多くの危険を無視します。
スーパーキャット

@supercat引用していたコメントに同意しませんでした。
ロブ・グラント

21

本番環境でアサーションを残すことを考えている場合でも、おそらくそれらを間違っていると考えているでしょう。アサーションの全体的なポイントは、それらがソリューションの一部ではないため、本番環境でオフにできることです。これらは開発ツールであり、想定が正しいことを確認するために使用されます。しかし、本番環境に移行するときは、すでに想定に自信があるはずです。

とはいえ、本番環境でアサーションをオンにするケースが1つあります。本番環境で再現可能なバグが発生し、テスト環境で再現できない場合は、アサーションをオンにしてバグを再現すると役立つ場合があります。本番環境で、有用な情報を提供しているかどうかを確認します。

より興味深い質問は次のとおりです。テスト段階で、いつアサーションをオフにしますか?


4
アサーションは製品コードに含めるべきではないと思います。アサーションはエラーではなく、開発者向けに設計されています。アサーションはテストコードにのみ存在する必要があります。アプリがクラッシュするのは、アサーションが受け入れられない、ずさんな開発が原因です。エラーを適切に処理するには、開発者はさらに一歩前進する必要があります。
iksnae 2014年

9
fnにnullポインターが渡された場合にクラッシュすることが避けられない場合。明示的に処理するという選択肢はありません。条件を適切に処理する方法があるか(外界からの入力に起因する可能性があるため)、途中で破損した可能性のあるランダムな場所ではなく、アサートを使用してドキュメントの場所でクラッシュします。ただし、アサートの処理方法はモジュールごとに決定する必要があります。たぶん、ウォッチドッグがプロセスを再起動するか、そのモジュールのメモリのチャンクをワイプして、開始状態にリセットします(ソフトオブジェクト「再起動」)。
Rob

1
これはアサートの使用に関する限られた見方だと思います。私は常にコンソールとクラウドストレージの両方にアサートを記録し、本番環境ではオンのままにします。アサートを続けると、実稼働コードと実稼働使用においても、私の仮定が正しいままであることが確認されます。アサートをオンにしてコードがデバッグで数回正常に実行されたからといって、同じコードパスを介して異なる値を渡す方法がユーザーに見つからないわけではありません。
SafeFastExpressive 2018年

assertステートメントの要点は、チェックをオンまたはオフにできることです。本番環境でそれらをオンのままにする場合、なぜassertステートメントを使用するのですか?
MiguelMunoz 2018年

1
アサーションはしばしばシステムの速度を低下させます。これらは本番用に設計されていないため、低速で非効率的になることは自由であり、特定のテストの実行に必要になる場合があります。たとえば、MicrosoftはかつてExcelに高速再計算機能を追加しました。1つのセルが変更された場合、この機能は再計算をそれを必要とするセルのみに制限しました。彼らはスプレッドシート全体を再計算し、結果を比較するアサーションでこれをテストしました。これにより、開発バージョンの実行が非常に遅くなりましたが、多くのバグが取り除かれました。彼らがこの機能をリリースしたとき、それは非常に信頼できることが証明されました。
MiguelMunoz

16

アサーションは製品コードに留まるべきではありません。特定のアサーションがプロダクションコードで役立つと思われる場合は、アサーションであってはなりません。これは、実行時エラーチェック、つまり次のようにコーディングされたものでなければなりませんif( condition != expected ) throw exception

「アサーション」という用語は、「フィールドで実行されない開発時のみのチェック」を意味するようになりました。

アサーションがフィールドに到達する可能性があると考え始めると、必然的に、特定のアサーションが本当に作成する価値があるかどうか疑問に思うなど、他の危険な考えも開始します。作成する価値のない主張はありません。「これを断言すべきかどうか」と自問するべきではありません。「断言するのを忘れたものはありますか?」


6

アサーションがパフォーマンスの問題を引き起こしていることをプロファイリングが示していない限り、私はそれらも本番リリースにとどまるべきだと言います。

ただし、これには、アサーションの失敗をある程度優雅に処理することも必要だと思います。たとえば、プログラムを終了したりクラッシュさせたりするだけでなく、開発者に問題を(自動的に)レポートするオプションを備えた一般的なタイプのダイアログが表示されるはずです。また、実際に許可する条件にアサーションを使用しないように注意する必要がありますが、望ましくない、または望ましくないと考えてはいけません。これらの条件は、コードの他の部分で処理する必要があります。


私が見ているように、プロダクションアサーションの主な目的は緊急バックストップです。プログラムの続行を許可すると、プログラムが実行している他のどのプログラムよりもそれを防ぐことがより重要であるほど深刻な害が発生する可能性が非常に高くなります。可能であれば、適切なエラーメッセージが表示されればよいのですが、それは2番目に重要なことです。
スーパーキャット

5

私のC ++では、アサーションがリリースビルドで失敗した場合に例外をスローすることを除いて、assert(x)に似たREQUIRE(x)を定義しています。

アサーションの失敗はバグを示しているため、リリースビルドでも真剣に扱う必要があります。コードのパフォーマンスが重要な場合、高速で実行する必要がある高レベルのコードにはREQUIRE()を使用し、低レベルのコードにはassert()を使用することがよくあります。サードパーティが書き込んだコードから渡されたデータやファイルの破損が原因でエラー状態が発生する可能性がある場合は、アサートの代わりにREQUIREも使用します(最適な方法では、ファイルが破損した場合に適切に動作するようにコードを設計しますが、常にそうする時間はありません。)

彼らはあなたがそれらを理解しないのでそれらの断言メッセージをエンドユーザーに見せるべきではないと言います。そう?エンドユーザーは、スクリーンショットまたはエラーメッセージのテキストを含むメールを送信する場合があり、デバッグに役立ちます。ユーザーが単に「クラッシュした」と言っただけでは、修正する能力が低くなります。アサーションエラーメッセージをインターネット経由で自動的に自分に送信することをお勧めしますが、これは、ユーザーがインターネットにアクセスでき、許可を得ることができる場合にのみ機能します。


彼らはまた、彼らが破壊するための貴重な手がかりをしているので、あなたがハッカーにそれらアサートメッセージを表示してはならないと言う。
DaveWalley

4

それらを保持したい場合は、エラー処理に置き換えます。プログラムが消えるだけで最悪です。特定のエラーを深刻なバグとして扱うことには何の問題もないと思いますが、データを収集してログに記録し、アプリに望ましくない状態が発生したことをユーザーに通知することで対処するように備えられているプログラムのセクションに転送する必要があります。終了します。


2

他のエラーと同様に処理されれば、問題は発生しません。他の言語と同様に、Cでの失敗したアサーションはプログラムを終了するだけであり、これは通常、実動システムでは不十分です。

いくつかの例外があります。たとえば、PHPではアサーションエラーのカスタムハンドラーを作成できるため、単に終了するだけでなく、カスタムエラーを表示したり、詳細なログを作成したりできます。


2

当社のデータベースサーバーソフトウェアには、本番アサーションとデバッグアサーションの両方が含まれています。デバッグアサーションはそれだけです。本番コードでは削除されています。本番アサーションは、(a)存在してはならない状態が存在し、(b)この状態から確実に回復できない場合にのみ発生します。製品のアサーションは、ソフトウェアのバグが発生したか、または何らかのデータ破損が発生したことを示しています。

これはデータベースシステムであり、エンタープライズクリティカルな可能性のあるデータを格納しているため、データの破損を防ぐためにできる限りのことを行います。不適切なデータを保存する可能性のある状態が存在する場合、すぐにすべてのトランザクションをアサートし、ロールバックして、サーバーを停止します。

そうは言っても、パフォーマンスが重要なルーチンでは本番アサーションを回避しようとします。


5
私はあなたの「プロダクションアサーション」を「例外」と呼んで、そのようにコーディングします。
DaveWalley、2014

1
あなたはおそらく正しいですが、製品は元々Cで書かれていました。C++に変更した場合でも、一部のプラットフォームで使用したコンパイラは例外を適切にサポートしていませんでした。古いコードのほとんどはC ++で書き換えられなかったため、これらのアサーションは引き続き使用されます。
Graeme Perrow、2014

1

アサートをインラインユニットテストと見なします。開発中の迅速なテストに役立ちますが、最終的にはこれらのアサーションをリファクタリングして、ユニットテストで外部からテストする必要があります。


主張:事実または信念を述べる。これらはテスト(のみ)のためではなく、あなたが真であると考えるもの(つまり、仮定)を述べるためです。そのため、何らかの理由で仮定が間違っていてもプログラムは続行されません。たとえば、これはアサーションの有効な使用です:assert(pow(1,0)<1)。エラーチェックに適切な場所ではありません。それが真実でない場合、現代の数学のほとんどすべてが間違っているためです。まあ、どうすればそれを処理し始めますか?その誤った仮定の処理はプログラムの範囲外です。あなたは信仰をもってそれをとります。しかし、とにかく確認します。

1

範囲内にあるすべてのエラーを処理し、アサーションを使用して、私たちがARE trueであると仮定していると仮定します。

つまり、プログラムがファイルを開いたり、読み込んだり、閉じたりしている場合、そのファイルを開くことができないことはスコープ内にあります-言い換えると、無視するのは怠慢なのです。したがって、エラーチェックコードが関連付けられているはずです。

ただし、fopen()が常に有効なオープンファイルハンドルを返すと記載されているとします。ファイルを開き、それをreadfile()関数に渡します。

このコンテキストでのreadfile関数は、おそらくその設計仕様によると、有効なファイルptrを取得するとほぼ想定できます。したがって、このような単純なプログラムで、否定的なケースのエラー処理コードを追加することは無駄です。ただし、実行を続行する前に、少なくとも仮定を文書化する必要があります。誤って呼び出された場合や、たとえば他のプログラムにコピー/貼り付けされた場合に備えて、が常に有効であると実際に想定するべきではありません。

したがって、readfile(){assert(fptr!= NULL); この場合は..}が適切ですが、本格的なエラー処理は適切ではありません(実際にファイルを読み取るには何らかのエラー処理システムが必要になるという事実を無視します)。

そして、はい、それらのアサーションは、それらを無効にすることが絶対に必要な場合を除いて、本番用コードに留まる必要があります。それでも、パフォーマンスが重要なセクション内でのみ無効にする必要があります。


1

コードの一部が本番環境にあり、通常はトリガーされるアサーションにヒットするとします。アサーションでバグが見つかりました!アサーションがオフになっているため、そうではありません。

それで今何が起こるのでしょうか?プログラムは、(1)問題の原因からさらに離れた時点で、情報を提供しない方法でクラッシュするか、(2)正常に完了まで実行され、おそらく間違った結果をもたらします。

どちらのシナリオも魅力的ではありません。本番環境でもアサーションをアクティブのままにします。


0

アサーションは、コンパイル時の型チェック以外のものにはほとんど使用しません。ほとんどの言語がそれらを処理するように構築されているからといって、アサーションの代わりに例外を使用します。

例を挙げます

file = create-some-file();
_throwExceptionIf( file.exists() == false, "FILE DOES NOT EXIST");

に対して

file = create-some-file();
ASSERT(file.exists());

アプリケーションはアサーションをどのように処理しますか?私try catchは、致命的なエラーを処理する古い方法を好みます。


2
例外は、この例のように、動作中のアプリケーションで発生すると予想される異常な状況に対するものです。アサーションは、決して遭遇しないと予想される状況用です。したがって、それらに遭遇した場合、コードにバグがあるはずです。あなたの例では、例外は明らかに正しいアプローチです。しかし、アサーションはまだバグをキャッチするのに非常に役立ちます。
MiguelMunoz 2014

0

ほとんどの場合、Javaでアサーション(assertキーワード)を使用すると、後でいくつかの製品コードが自動的に追加されます。場合によっては、ログメッセージ、例外、または何もありません。

私によると、すべてのアサーションは、製品リリースではなく開発リリースで重要です。それらのいくつかは保持する必要があり、他は破棄する必要があります。


0

アサーションはエラーではないため、エラーとして処理しないでください。アサーションがスローされた場合、これは、コードまたはコードを呼び出すコードにバグがあることを意味します。

本番コードでアサーションを有効にしないようにするためのポイントがいくつかあります。1.エンドユーザーに「ASSERTION failed MyPrivateClass.cpp line 147」のようなメッセージが表示されないようにします。エンドユーザーはQAエンジニアではありません。2。アサーションはパフォーマンスに影響を与える

ただし、アサーションを残すべき1つの大きな理由があります。ASSERTIONはパフォーマンスとタイミングに影響を与える可能性があり、悲しいことにこれが問題になる場合があります(特に組み込みシステムの場合)。

私はアサーションをプロダクションコードに残すことに投票する傾向がありますが、これらのアサーションの印刷出力がエンドユーザーに公開されないようにしてください。

〜Yitzik


いいえ、主張は仮定された事実のためのものです。私たちのコードが常に25を返すと想定されているときに27を返す場合、問題は、宇宙に関する私たちの物理的な仮定のバグである可能性もあります。おそらく、これらの2ビットが、計算の歴史において初めて5つの可能な値に切り替わった可能性があります。アサーションは、コードが記述された前提の下でコードがまだ動作していることを確認するためのものです。物理演算がウィンドウの外に出た場合、コードに気づき、ドライブをそのままにし、先に進んでいる間は終了する必要があります;)しかし、はい、それはコードのエラーではなく、どのようなエラー処理を実行できますか?

私の意見を洗練させてください:1.アサーションは私たちの仮定をチェックします。私たちの仮定が間違っている場合、これは私たちのコードにバグがあることを意味します。2.私たちのコードは、コードの使用を主張するべきではありません。つまり、ユーザーからの入力に問題がある場合、関数はアサーションで失敗してはなりません。エラーを返し、ユーザーは処理する必要があります(成功を表明できます)3.アサーションを本番環境に残すことをお勧めしますが、動作はおそらく変更されます。適切なエラー処理がないことに同意します。失敗したアサーション==バグ..しかし、システムが停止して再起動を待機する代わりに、システム自体を再初期化する場合があります。
Yitshak Yarom

1
必ずしもバグがあるという意味ではありません。たとえば、私が関わっているプロジェクトの多くには、ドキュメントに想定される事実のリストが含まれています。これらの前提は、たとえば、ビジネスの人々が間違ったことを言った可能性がある場合や、特定の変数に関するサードパーティからの情報がない場合などに、開発者のコ​​ードがバギーと呼ばれるのを防ぐためにあります。アサーションを使用して、ITが正しいかどうかだけでなく、プログラムを実行する必要があるかどうか、サードパーティのシステムが正しいかどうかを確認できます。

-8

アサーションはエラーであり、純粋で単純であるため、アサーションは同じように処理する必要があります。

エラーはリリースモードで処理する必要があるため、アサーションは実際には必要ありません。

アサーションについて私が見る主な利点は、条件付きの中断です。これらは、VCのウィンドウをドリルスルーして1行のコードで何かをセットアップするよりもはるかに簡単にセットアップできます。


2
条件付きブレークポイントとしてアサートを使用することは、いくつかの理由で本当に面倒です。最も重要なのは、これらのアサートがチーム内の他の開発者を混乱させることです。そのアサートが発生したときにエラーであるか、または誰かが条件付きブレークポイントをコードに残したことがエラーであるかをどうやって知るのでしょうか。
レゴ

そもそもアサートがコードになかった場合はありません。そして、それらを使用してコードを監視している場合は、それらだけが表示されます(ソースツリーにチェックインしていない場合)。私は事実上すべてを主張する場所で働いてきました。ヘルプドキュメントサーバーが利用できないため、プログラムの開始時に約60回クリックする必要があり、すぐに飽きてしまいます。
graham.reeds
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.