こんにちは、私の名前はDmitry Karlovskyです。残念ながら、大きな記事を書く時間はありませんが、いくつかのアイデアを共有したいと思っています。 それでは、ちょっとしたプログラミングのメモをテストしてみましょう。 今日は自動テストについてお話します:
- なぜテストを書くのですか?
- テストとは何ですか?
- どのようにテストを書くのですか?
- 彼らはどのように書く価値がありますか?
- ユニットテストが悪いのはなぜですか?

自動テストタスク
より重要なものから少ないものへ:
- できるだけ早く欠陥を検出します。 ユーザーが見る前、サーバーに配置する前、テスト用に与える前、コミットする前。
- 問題のローカライズ。 テストはコードの一部のみに影響します。
- 開発をスピードアップします。 テストの実行は、手動検証よりもはるかに高速です。
- 実際のドキュメント。 このテストは、シンプルで保証された最新の使用例です。
直交分類
- オブジェクト分類
- 試験タイプによる分類
- テストプロセスのタイプによる分類
念のため、自動テストについてのみ話していることを強調します。
テストオブジェクト
- モジュールまたはユニットは、残りのコードとは独立してテストできる最小のコードです。 単体テストは単体テストとも呼ばれます。
- コンポーネントは、アプリケーションの比較的独立した部分です。 他のコンポーネントおよびモジュールが含まれる場合があります。
- アプリケーションまたはシステムは、他のすべてのコンポーネントを間接的に含むコンポーネントの縮退したケースです。
試験の種類
- 機能 -機能要件への準拠を確認します
- 統合 -隣接するテストオブジェクトの互換性の確認
- ロード -パフォーマンス要件への準拠を確認します
テストプロセスの種類
- 受け入れ -新しい/変更された機能の確認。
- 回帰 -変更されていない機能に欠陥がないことを確認します。
- 煙 -明らかな欠陥がないか基本的な機能を確認します。
- 完全 -すべての機能を確認します。
- 構成 -さまざまな構成のすべての機能を確認します。
テスト数
- テストはコードです。
- コードの記述には時間がかかります。
- コードをサポートするには時間がかかります。
- コードにはエラーが含まれる場合があります。
テストが多いほど、開発は遅くなります。
テストの完全性
- テストでは、すべてのユーザーシナリオを検証する必要があります。
- テストは、ロジックの各ブランチに行く必要があります。
- テストでは、すべての同等クラスをチェックする必要があります。
- テストでは、すべての境界条件を確認する必要があります。
- テストでは、非標準条件への応答をテストする必要があります。
テストが完了すると、リファクタリングとテストが高速になり、その結果、新しい機能が提供されます。
ビジネスの優先事項
- 開発速度を最大化します。 開発者は、迅速に実行される最小限のテストを記述する必要があります。
- 欠陥の最小化。 最大のカバレッジを提供する必要があります。
- 開発コストを最小限に抑えます。 コード(テストを含む)の作成と保守に最小限の労力を費やす必要があります。
テスト戦略
優先順位に応じて、いくつかの主要な戦略を区別できます。
- 品質 。 すべてのモジュールの 機能テストを作成します 。 統合テストとの互換性を確認します。 すべての非縮退コンポーネントのテストを追加します 。 コンポーネントの 統合を忘れないでください。 アプリケーション全体をテストします 。 マルチレベルの包括的なテストには多くの時間とリソースが必要ですが、欠陥を特定する可能性が高くなります。
- スピード 。 アプリケーションの煙テストのみを使用します。 主な機能が動作することは確かであり、突然の場合は残りを修正します。 したがって、機能を迅速に提供しますが、それを実現するために多くのリソースを費やしています。
- コスト 。 アプリケーション全体に対してのみテストを作成します。 したがって、重大な欠陥が事前に検出されるため、サポートのコストが削減され、その結果、新機能の提供が比較的高速になります。
- 品質とスピード 。 すべての(縮退を含む) コンポーネントをテストでカバーします。これにより、最小限のテストで最大限のカバレッジが得られるため、高速で最小限の欠陥が得られるため、比較的低コストになります。
応用例
私の分析が完全に根拠がないように、2つのコンポーネントの最も単純なアプリケーションを作成しましょう。 名前入力フィールドと、その名前に宛てられた挨拶の出力を含むブロックが含まれます。
$my_hello $mol_list rows / <= Input $mol_string value?val <=> name?val \ <= Output $my_hello_message target <= name - $my_hello_message $mol_view sub / \Hello, <= target \
この表記法が初めての方は、同等のTypeScriptコードをご覧になることをお勧めします。
export class $my_hello extends $mol_list { rows() { return [ this.Input() , this.Output() ] } @mem Input() { return this.$.$mol_string.make({ value : next => this.name( next ) , }) } @mem Output() { return this.$.$my_hello_message.make({ target : ()=> this.name() , }) } @mem name( next = '' ) { return next } } export class $my_hello_message extends $mol_view { sub() { return [ 'Hello, ' , this.target() ] } target() { return '' } }
@mem
は、リアクティブキャッシュデコレータです。 this.$
はdiコンテキストです。 バインドは、プロパティの再定義を通じて行われます。 .make
は、指定されたプロパティを単にインスタンス化し、オーバーライドします。
コンポーネントテスト
このアプローチでは、可能な限り実際の依存関係を使用します。
どんな場合でも何を浸すべきですか:
- 外の世界との相互作用(http、localStorage、場所など)
- 非決定的(Math.random、Date.nowなど)
- 特に遅いもの(暗号化ハッシュの計算など)
- 非同期(同期テストは理解とデバッグが簡単です)
そのため、最初にネストされたコンポーネントのテストを作成します。
// Components tests of $my_hello_message $mol_test({ 'print greeting to defined target'() { const app = new $my_hello_message app.target = ()=> 'Jin' $mol_assert_equal( app.sub().join( '' ) , 'Hello, Jin' ) } , })
次に、外部コンポーネントのテストを追加します。
// Components tests of $my_hello $mol_test({ 'contains Input and Output'() { const app = new $my_hello $mol_assert_like( app.sub() , [ app.Input() , app.Output() , ] ) } , 'print greeting with name from input'() { const app = new $my_hello $mol_assert_equal( app.Output().sub().join( '' ) , 'Hello, ' ) app.Input().value( 'Jin' ) $mol_assert_equal( app.Output().sub().join( '' ), 'Hello, Jin' ) } , })
ご覧のとおり、必要なのはパブリックコンポーネントインターフェイスだけです。 どのプロパティと値が出力に渡されるかは気にしません。 要件を正確に確認します。表示されたグリーティングがユーザーが入力した名前と一致すること。
単体テスト
単体テストの場合、モジュールを残りのコードから分離する必要があります。 モジュールが他のモジュールと相互作用しない場合、テストはコンポーネントのテストと同じです。
// Unit tests of $my_hello_message $mol_test({ 'print greeting to defined target'() { const app = new $my_hello_message app.target = ()=> 'Jin' $mol_assert_equal( app.sub().join( '' ), 'Hello, Jin' ) } , })
モジュールが他のモジュールを必要とする場合、それらはスタブに置き換えられ、それらとの通信が期待どおりであることを確認します。
// Unit tests of $my_hello $mol_test({ 'contains Input and Output'() { const app = new $my_hello const Input = {} as $mol_string app.Input = ()=> Input const Output = {} as $mol_hello_message app.Output = ()=> Output $mol_assert_like( app.sub() , [ Input , Output , ] ) } , 'Input value binds to name'() { const app = new $my_hello app.$ = Object.create( $ ) const Input = {} as $mol_string app.$.$mol_string = function(){ return Input } as any $mol_assert_equal( app.name() , '' ) Input.value( 'Jin' ) $mol_assert_equal( app.name() , 'Jin' ) } , 'Output target binds to name'() { const app = new $my_hello app.$ = Object.create( $ ) const Output = {} as $my_hello_message app.$.$mol_hello_message = function(){ return Output } as any $mol_assert_equal( Output.title() , '' ) app.name( 'Jin' ) $mol_assert_equal( Output.title() , 'Jin' ) } , })
モッキングは無料ではありません-より複雑なテストにつながります。 しかし、最も悲しいことは、mokasで作業をチェックした後、実際のモジュールではこれらすべてが正しく機能することを確認できないことです。 気をつけていれば、最後のコードでは、名前がtitle
プロパティを介して渡されることを期待していることにすでに気付いています。 そして、これは2種類のエラーにつながります。
- 正しいモジュールコードを使用すると、mokでエラーが発生する可能性があります。
- 欠陥のあるモジュールコードは、モックでエラーを生成しない場合があります。
そして最後に、テストでは、要件を確認せず(置換された名前のグリーティングを表示する必要があることを思い出します)、実装(そのようなメソッド内で、そのようなパラメーターで呼び出されます)を確認します。 これは、テストが脆弱であることを意味します。
壊れやすいテストとは、同等の実装が変更されると破損するテストです。
同等の変更とは、機能要件のコード準拠を壊さない実装の変更です。
テスト駆動開発
TDDアルゴリズムは非常にシンプルで非常に便利です。
- テストを作成し、クラッシュすることを確認します。これは、テストが実際に何かをテストし、コードの変更が本当に必要であることを意味します。
- テストの落下が停止するまでコードを記述します。つまり、すべての要件を満たしています。
- コードをリファクタリングし、テストが失敗しないことを確認します。つまり、コードはまだ要件に準拠しています。
脆弱なテストを作成すると、リファクタリングの段階でテストが常に失敗し、調査と調整が必要になり、プログラマの生産性が低下します。
統合テスト
単体テストの後に残ったケースを克服するために、追加のタイプのテスト-統合テストを思いつきました。 ここでは、いくつかのモジュールを取り、それらが正しく相互作用することを確認します。
// Integration tests of $my_hello $mol_test({ 'print greeting with name'() { const app = new $my_hello $mol_assert_equal( app.Output().sub().join( '' ) , 'Hello, ' ) app.Input().value( 'Jin' ) $mol_assert_equal( app.Output().sub().join( '' ), 'Hello, Jin' ) } , })
ええ、最後のコンポーネントテストを取得しました。 つまり、何らかの方法で、要件をチェックするすべてのコンポーネントテストを作成しましたが、さらにテストのロジックの具体的な実装を修正しました。 これは通常冗長です。
統計
基準 | カスケードコンポーネント | モジュラー+統合 |
---|
クロス | 17 | 34 + 8 |
複雑さ | シンプル | 複雑な |
カプセル化 | ブラックボックス | ホワイトボックス |
もろさ | 低い | 高い |
カバレッジ | いっぱい | 追加 |
速度 | 高い | 低い |
期間 | 低い | 高い |
関連リンク