ダイアディック関数assertEquals(expected、actual)に伴う問題の解決


10

長年にわたるカウボーイコーディングの後、私は良質のコードを書く方法についての本を手に入れることにしました。私はRobert Cecil MartinのClean Codeを読んでいます。第3章(関数)には、2項関数に関するセクションがあります。これは本からの抜粋です。

のような明白な二項関数でさえassertEquals(expected, actual)問題があります。予想されるはずの場所に実際の回数を何回入れましたか?2つの引数には自然な順序はありません。予想される実際の順序は、慣れるために習得する必要がある規則です。

著者は説得力のあるポイントを作成します。私は機械学習で働いており、これにいつも出会います。たとえば、sklearnライブラリのすべてのメトリック関数(おそらくフィールドで最も使用されているpythonライブラリ)では、入力の順序に注意する必要があります。例として、sklearn.metrics.homogeneity_scoreは入力labels_trueおよびとして使用されlabels_predます。この関数の機能はあまり重要ではありません。重要なのは、入力の順序を切り替えてもエラーがスローされないことです。実際、入力を切り替えることは、ライブラリ内の別の関数を使用することと同じです。

ただし、この本では、のような関数の賢明な修正については触れていませんassertEqualsassertEquals上記のような頻繁に遭遇する機能の修正については考えられません。この問題を解決するための良い方法は何ですか?

回答:


11

修正がない場合でも問題が発生する可能性があることに注意することをお勧めします。このようにして、そのようなコードを読み書きするときに注意することができます。この特定の例では、しばらくすると引数の順序に慣れるだけです。

パラメータの順序に関する混乱を防ぐための言語レベルの方法があります。名前付き引数です。残念ながら、これはJavaやC ++などのCスタイルの構文を持つ多くの言語ではサポートされていません。しかし、Pythonでは、すべての引数を名前付き引数にすることができます。関数をdef foo(a, b)として呼び出す代わりにfoo(1, 2)、私たちは行うことができますfoo(a=1, b=2)。C#のような最近の言語の多くは、同様の構文を持っています。Smalltalk言語ファミリは、名前付き引数を最も広く使用しています。位置引数はなく、すべて名前付きです。これにより、自然言語に非常に近いコードを読み取ることができます。

より実用的な方法は、名前付き引数をシミュレートするAPIを作成することです。これらは、流れるようなAPI、または自然な流れを作成するヘルパー関数にすることができます。assertEquals(actual, expected)名前が混乱しています。私が見たいくつかの選択肢:

  • assertThat(actual, is(equalTo(expected))):一部の引数をヘルパー型でラップすることにより、ラッピング関数は効果的にパラメーター名として機能します。ユニットテストアサーションの特定のケースでは、この手法はHamcrestマッチャーによって使用され、拡張可能で構成可能なアサーションシステムを提供します。ここでの不利な点は、ネストが多くなり、多くのヘルパー関数をインポートする必要があることです。これはC ++での私の頼りになるテクニックです。

  • expect(actual).to.be(expected):関数呼び出しをひもでつなぐ流暢なAPI。これにより余分な入れ子が回避されますが、これはあまり拡張可能ではありません。流暢なAPIは非常に読みやすいと思いますが、通話チェーンで非終端状態の追加のクラスを実装する必要があるため、流暢なAPIの設計は私の経験で多くの労力を費やす傾向があります。この取り組みは、次の許可されたメソッド呼び出しを提案できるオートコンプリートIDEのコンテキストでのみ効果を発揮します。


4

この問題を回避する方法はいくつかあります。呼び出すメソッドの変更を強制しないもの:

のではなく

assertEquals( 42, meaningOfLife() ); 

使用する

expected = 42;
actual = meaningOfLife();
assertEquals(expected, actual);

これにより、規則が強制的にオープンになり、切り替えが行われていることが簡単にわかります。もちろん、書くのは簡単ではありませんが、読むのは簡単です。

呼び出されるメソッドを変更できる場合は、タイピングシステムを使用して、読みやすい使用法を強制できます。

assertThat( meaningOfLife(), is(42) );

一部の言語では名前付きパラメーターがあるため、これを回避できます。

assertEquals( expected=42, actual=meaningOfLife() );

他のものはそうしないので、それらをシミュレートします:

assertEquals().expected(42).actual( meaningOfLife() );

何を読んでも、どれが正しいかを明らかにする方法を見つけます。規約が何であるかを推測させないでください。見せてください。

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