
機能テストを作成する過程で、さまざまなテーブルのデータの正確性をチェックすることがよくありました。 テーブルはWebページ、データベース、またはExcelファイルにあります。 いずれにせよ、それらの内容が指定されたもの、つまり、テストシナリオで作成されたものに対応することを確認する必要がありました。
この投稿では、hamcrestライブラリを使用してこのようなチェックを記録する方法とその理由について説明します。
最初はすべてが簡単でした。
タイプチェック:列「給与」の15行目には、「百万」があるはずです。 これを行うために、私はこのようなメソッドを持っていました:
assertCellByColumnAndRowNumber
、あちこちでうらやましい規則性で複製されました。
その後、すべてが少し複雑になり、行の番号ではなく主キーで確認する必要がありました。「名前」の「給与」列の「ヴァシリーイワノビッチ」は「100万」である必要があります。
assertCellByColumnAndPrimaryKey
メソッドは生まれましたが、もちろん1か所で生まれたわけではありません。
次に、主キーが複合である場所がありました。 複合キーで機能するメソッドは、入力としてさらに多くの値を取り始め、コードを理解することがより困難になりました。
次のことを確認する必要がある場合、ケースを終了しました。「ステータス」が「OK」の行の場合、「タイプ」列には値「A、B、C」が上から下の順に含まれます。
非常に長い名前と変数の束を使用して別のメソッドを作成することは可能ですが、これで終わりではなく、メソッドが増え、テスト
を記述してサポートするのがますます難しくなることを理解し始めました。
したがって、テーブルの条件を統一された形式で書き留め、最終的にさまざまなチェックを行う
一連のメソッドを取り除くために、
hamcrestを使用することにしました。
次のように、Type列のチェックを書き留めました。
assertThat( table, column("Type",contains("A","B","C")).where(cell("Status", is("Ok"))) );
これがどのように機能するかについて詳しく説明します。
table
は行のコレクションで表されます(
class Table extends Collection<Row>
)
このようなテーブルの検証を記録するために、行またはテーブル全体の条件を指定する
ハムクレスト マッチャーを作成しました。
これまでのところ、次のプレーヤーで十分です。
CellMatcher
は、1つの行セルの条件を定義します。
例: cell("Id", greaterThan(0))
列「Id」に0より大きい値が含まれる場合、行を選択します。
このようなマッチャーを作成するには、列の名前と、この列の値をチェックする「標準」ハムクレストマッチャーを指定する必要があります。
このゲーマーと標準コレクションゲーマーを使用して、テーブル全体の状態を記録できます。
たとえば、コレクションの各要素(テーブルの行)を特定のルールに準拠しているかどうかをチェックする「ライブラリ」everyItemを使用して、次の条件を記述できます。
テーブルの各行で、Id値はゼロより大きく、Timeは空ではありません( null
はありません):
everyItem(both(cell("Id", greaterThan(0))).and(cell("Time", notNullValue())))
また、標準のCombinableMatcher
の機能CombinableMatcher
、この条件はさらに簡単に記述されます。 everyItem(cell("Id", greaterThan(0)).and(cell("Time", notNullValue()))))
FilterMatcher
-1つの一致に基づいてテーブルをフィルタリングし、2番目のマッチャーを残りの行に適用します。
最初のマッチャー(フィルター)として、 CellMatcher
、または複数のCellMatcher
の和集合CellMatcher
ます。
FilterMatcher
を使用すると、前の例を次のように書き換えることができます。 where(cell("Id",greaterThan(0)),everyItem(cell("Time",notNullValue())))
この場合、Id> 0のすべてのシリーズでTimeが空ではない( null
ない)ことを確認しnull
。 Idが0または負の場合、前の例とは異なり、Timeは空になる場合があります。
ColumnMatcher
は、テーブルの1つの列のすべての値の条件を設定します。
例: column("Action", contains("Active", "Pause", "Active", "Closed"))
「アクション」列に「アクティブ」、「一時停止」、「アクティブ」、「クローズ」の順に値が含まれる条件に従って、条件を設定します。
contains
標準ライブラリの代わりに、 containsInAnyOrder, hasItem
など、コレクションの他のマッチャーを使用できます(列はオブジェクトの1次元コレクションとして表示されます)。
もちろん、このような条件にフィルターを追加できます。 column("Action", contains("Active", "Closed")).where(cell("Id",greaterThan(2)))
したがって、IDが2より大きい行については、アクション列に「アクティブ」、「クローズ」の順に値が含まれていることを確認します。
ColumnMatcher
使用すると、集計マッチャーを使用して、列の量、最小、最大の条件をエレガントに確認できます。 例えば column("Salary", sum(is(100000))).where(cell("Type",is("fulltime")))
従業員のフルタイム給与の金額を確認できます。
ColumnsMatcher
使用ColumnsMatcher
と、テーブルからいくつかの列を切り取り、結果の2次元データ配列の条件を設定できColumnsMatcher
。 例: sliced(byColumns("Action", "Time"), contains(row("Pause", "12:00"), row("Active", "12:30"), row("Closed", "14:00"))) .where(<some condition>)
ここでは、おそらく非常に大きなテーブル全体から選択し、列「Action」と「Time」のみを使用して、明確に定義された値が含まれていることを確認します。
- なぜなら テーブルは標準のコレクションです。たとえば
not(empty()),iterableWithSize(lessThan(10))
標準のハムレストマッチャーを使用してnot(empty()),iterableWithSize(lessThan(10))
を使用し、自転車を再発明しないで、行数の条件を設定できます。
マッチャーを作成するのに1日半かかりました。それ自体は非常に興味深いアクティビティであり、デザインパターンの優れたプラクティスとして役立ちました。 テーブルを提示するほうが良いという瞬間から、多くのアーキテクチャ上の設計上の決定をしなければなりませんでしたか?
おそらく、コード行あたりのアーキテクチャソリューションの数に基づいて、1日半で最も豊かでしたが、15キロバイト未満であることが判明しました。
一般的に、これは、いくつかのリファクタリングサイクルと、書面によるマッチャーの作成および適用中に必要な設計変更を伴う、別個のミニプロジェクトであることが判明しました。 私は実際に私の最初のTDDを使用して、小さなユニットテストを作成しました。
これは、初心者がTDD(およびその他の慣行)を勉強するための素晴らしい例、または回答をよく知っている候補者の建築的、「設計」能力を特定するための面接での実践的な質問(私が実施しなければならない)の良いトピックになると思いましたなぜ下水道マンホールが丸いのかという質問に。 (冗談です、私は彼に決して尋ねません、あなたは?)。
結論:
説明されているテーブルマッチャーは許可されています。
- テストで出会ったテーブルのすべてのチェックを書き留め、
- コードの重複を取り除き、
- コードを短くして理解しやすくする
- 問題の原因となった行またはセルをより正確に示すことで、エラーメッセージをより明確にします。
- 「 BDD vice versa 」アプローチの一部として使用するすべてのチェックの説明を含むログを自動的に取得します。これについては、次に説明します。
