記事で使用される用語のビット:
- アプリケーションは、単一の論理ユニットである場合、IBM Notes / Dominoデータベースまたはデータベースのコレクションです。
- クライアントは、IBM(Lotus)Notesソフトウェアのシッククライアントです。
- サーバー-IBM Dominoサーバーソフトウェア。
誰もが自分自身を見ることができるように、開発がどのように見えるかの説明から始めます。 まず、@-式とLotusScript言語を使用してシッククライアント用のアプリケーションを作成することです。 どちらの言語もスクリプト化されており、UIオブジェクト(フォームや表現など)のイベントやアクション、ライブラリ(LotusScript言語のみ)、エージェント、データベース設計の他の要素など、ほぼどこにでもコードを記述できます。 さらに、イベントとアクションごとに、記述する言語を個別に選択できます。 その結果、コードを整理するための何らかの方法がない場合、考えられないあらゆる場所に散らばったコードからひどい麺のような混乱が生じます。 そして今、これにアプリケーションの大きな動物園と、通常は専門家の小さなスタッフを追加します。
私は、コードを書くための特定のアプローチをワクチン接種しました(非常に感謝しています)。 その結果、どこで書くかだけでなく、どのように書くかについて話すシステム全体ができます。 要するに:
- すべてのビジネスロジックはLotusScriptで記述されているため、@-式の使用は最小限です。
- すべてのコードはライブラリ内にあり、以下に分割されます。
- サーバーとクライアントの両方で使用できるコード。
- クライアントでのみ使用できるコード。
- 視覚要素(フォーム、表現)では、ライブラリコードの呼び出しのみが使用され、大部分は単一行です。
- オブジェクトの最大使用と、LotusScript言語の手続き機能の最小使用。
それでは、単体テストはそれと何の関係があるのでしょうか? より複雑なマルチリンクシステムが得られるほど、すべてのコンポーネントを頭に収めることが難しくなります。 必然的に、ある場所で編集すると別の場所で故障が発生します。 多くの場合、何が起こっているのか、リソースの限られたパラメーターと依存関係の数が多いため、すべてのバリエーションを手動でテストすることはほとんど不可能です。 変更の恐れを一掃することはできません。なぜなら、基礎となるコードの機能を知っていることはもちろん、記憶することは常に可能ではないからです。 テストの存在は、相互作用の境界の概要を示し、操作性のテストを保証します。 はい、これはテストとその準備をカバーするための追加の努力ですが、私には思えるが、彼らはそれだけの価値がある。
Lotus Notes用IBM Notes / Dominoのユニットテストツールを正確に見つけるのを手伝ったGoogleの友人は、 OpenNTF.orgでプロジェクトの非常に古いバージョンを1つだけ作成しました 。 一般的に、私の頭の周りを回っていたアイデアは似ていましたが、実装はもう少し約束しました。 したがって、私はこの言葉を恐れずに自分で書くことに決めました。
テストのロジックを構築するための基礎は、(おそらく)単体テストフレームワーク(テストメソッドを持つクラス)で通常どおりに採用されました。 これらのテストは、ヒープに収集して実行する必要があります。 それで、私の頭の中で、最小限のセットに必要なもののリストを作りました。
- テストはクラスメソッドとして記述されます。
- テストはエージェントによって実行されます。
- テストを作成する際には、一連のチェック(アサーション、アサート)が必要になります。
- テスト実行の一部として、テストの成功に関する統計を保持し、エラーが発生したテストに行を表示する必要があります。
実装を設計するときに行われたいくつかの側面と仮定:
- LotusScriptには、武器庫にリフレクションも注釈もありません。つまり、どのメソッドがテストメソッドであるかを明示的に示す必要があります。 たとえば、少なくとも最初の段階では、テストクラスを使用したライブラリの解析オプションが却下されます。
- チェックの最小セット:
- 表現と真実の比較;
- 嘘。
- 平等の比較。 なぜなら 平等の問題は状況依存の問題であり、オブジェクトは平等のために比較メソッドに転送されます。 このオブジェクトのクラスは、ブール値を返す1つのパブリックメソッドを使用して、インターフェースクラス(LotusScript言語にはクリーンなインターフェースはありません)から継承する必要があります。
- エラーチェック、つまりエラーコード。
実装中に、質問が発生しました:統計を記録する方法は? 繰り返しますが、反射と注釈の手段はありません。
- 統計付きのコードで実行するエージェントで各テストメソッドをラップするか、
- これをテストメソッド自体に追加します。
二番目の道を行くことにしました。 その結果、AbstractTestクラスは、2つのパブリックメソッド、BeginTest(TEST_NAME)、EndTest(TEST_NAME)で生まれました。 なぜ公開するのですか? これは、組み込みのEclipseベースのIDEの機能により、便宜上行われました。スーパークラスのプライベートメソッドは子孫に表示され、再定義に使用できますが、IDE自体のヒントには到達しません。 成功したシナリオの各テストメソッドのテンプレートは次のようになります。
Public Sub TestMethod() On Error GoTo ErrorHandler Const FuncName = "TestClassName.TestSubName ()" Call Me.BeginTest(funcName) ' Call Me.EndTest(funcName) GoTo endh ErrorHandler: Error Err, "(" & DESIGN & ") " & FuncName & ", line " & Erl & Chr(10) & Error$ endh: End Sub
予想されるエラーのあるテストメソッドの場合、少し異なり、次のようになります。
Public Sub TestError() On Error GoTo ErrorHandler Const FuncName = "DemoTest.TestAssertErrorExample ()" Call Me.BeginTest(funcName) ' On Error ERROR_CODE_TO_TEST GoTo AssertErrorHandler ', , GoTo endh AssertErrorHandler: Call Me.EndTest(funcName) Resume endh ErrorHandler: Error Err, "(" & DESIGN & ") " & FuncName & ", line " & Erl & Chr(10) & Error$ endh: End Sub
これらの考慮事項から、テストの実行時間に関する情報を保存するテストコンテキストが作成されました。
- テストステータスデータ。
- テストを追加し、ステータスを変更する機能。
- ロギング。
テストの実行を担当するクラスもテンプレート化されています。 コードが最小化されている場合、メインのRun()メソッドと、DoRunTests()テストを実行するエージェントでオーバーライドされるメソッドで構成されます。 DoRunTestsメソッドには、テストクラスの作成と、対応するテストメソッドの呼び出しだけが含まれている必要があります。
Class AbstractTestRunner Public Function Run() On Error GoTo ErrorHandler Const FuncName = "AbstractTestRunner.Run ()" [...] Call Me.DoRunTests() [...] GoTo endh ErrorHandler: Error Err, "(" & DESIGN & ") " & FuncName & ", line " & Erl & Chr(10) & Error$ endh: End Function Private Sub DoRunTests() On Error GoTo ErrorHandler Const FuncName = "AbstractTestRunner.RunTests ()" 'Override GoTo endh ErrorHandler: Call Me.TestFailed(Error$) Resume Next endh: End Sub End Class
チェックは簡単です。 等値比較法のみが少し特別な注意に値します:
Public Sub Equals(matcher As IMatcher) On Error GoTo ErrorHandler Const FuncName = "Assert.Equals ()" If Not Matcher.Matches() Then Call Me.Throwerror(funcName) GoTo endh ErrorHandler: Error Err, "(" & DESIGN & ") " & FuncName & ", line " & Erl & Chr(10) & Error$ endh: End Sub
前述のように、クラスがインターフェイスクラス(IMatcher)から継承されたオブジェクトを受け取ります。このインターフェイスクラスには、ブール値としてMatches()というパブリックメソッドが1つだけあります。
Class IMatcher Public Function Matches() As Boolean End Function End Class
また、いくつかの単純なマッチャーが実装され、それらは別のライブラリに移動されました。
- SimpleValueMatcher-2つの値の単純な比較。 値はVariant型で受け入れられます。
- DocumentItemsMatcher-2つの指定されたドキュメントの指定されたフィールドの比較。 比較は、フィールドの存在、タイプ、および値で行われます。
- DatabaseMatcher-2つのデータベースの比較:レプリカID、パス、および名前。 一番下の行は、同じベースを扱っているということです。 ここにサーバー上の比較を追加することをお勧めします。
- DocumentMatcher-2つのドキュメントのIDの比較:それらは同じデータベースにあり、同じUniversalIdを持ち、すべてのフィールドで一致します。
このプロジェクトは、 GitHubで公開されており、Apache 2.0ライセンスで配布されており、インストールおよび使用のための詳細なドキュメントが提供されています。 このプロジェクトは、ドミノウォーターズで最も人気のあるプラットフォームOpenntf.orgにも投稿されました。
開発として私が見る:
- テストの作成を簡素化して、作成時に多数の規則を取り除くことができるようにします。
- テストクラスを手動で作成し、テストメソッドを呼び出す必要性を取り除きます。
- 集中化されたアプリケーションを作成して、テスト結果を実行および収集します。
そのため、Dominoの単体テストを作成する必要性全体を示し、提示されたツールの作成を勧めたシステムは、現在テストの対象外です。 しかし、私は最初に公開されるべき別のプロジェクトを準備しています。 最初のバージョンはすでに準備ができており、説明されているフレームワークで作成されたテストで100%カバーされています。 テストの統計を各リリースに適用します。
建設的なアイデア、提案、コメントはいつでも歓迎します!