7

私たちの JavaScript 開発者チームでは、純粋な機能コードを書くための redux/react スタイルを採用しています。ただし、コードの単体テストに問題があるようです。次の例を検討してください。

function foo(data) {
    return process({
        value: extractBar(data.prop1),
        otherValue: extractBaz(data.prop2.someOtherProp)
    });
}

processこの関数呼び出しは、 、 、および の呼び出しに依存してextractBarおりextractBaz、それぞれが他の関数を呼び出すことができます。data一緒に、それらは、テスト用に構築されるパラメーターの重要なモックを必要とする場合があり ます。

そのようなモック オブジェクトを作成する必要性を受け入れて、実際にテストで作成すると、読み取りや保守が困難なテスト ケースがあることがすぐにわかります。さらに、 の単体テストのように、同じことを何度もテストすることにつながる可能性が非常に高く、おそらくそれprocessも書かれるべきです。インターフェイスを介してこれらの関数によって実装される可能性のある各エッジ ケースのテストは扱いにくいです。extractBarextractBazfoo


いくつかの解決策を念頭に置いていますが、どちらも以前に見たパターンのようには見えないため、どれも気に入りません。

解決策 1:

function foo(data, deps = defaultDeps) {
    return deps.process({
        value: deps.extractBar(data.prop1),
        otherValue: deps.extractBaz(data.prop2.someOtherProp)
    });
}

解決策 2:

function foo(
    data, 
    processImpl = process, 
    extractBarImpl = extractBar, 
    extractBazImpl = extractBaz
) {
    return process({
        value: extractBar(data.prop1),
        otherValue: extractBaz(data.prop2.someOtherProp)
    });
}

foo解決策 2では、依存する関数呼び出しの数が増えると、メソッド シグネチャが急速に汚染されます。

解決策 3:

複雑な複合操作であるという事実を受け入れてfoo、全体としてテストしてください。すべての欠点が当てはまります。


他の可能性を提案してください。これは、関数型プログラミング コミュニティが何らかの方法で解決したに違いない問題だと思います。

4

1 に答える 1

7

検討した解決策はおそらく必要ありません。関数型プログラミングと命令型プログラミングの違いの 1 つは、関数型スタイルは推論しやすいコードを生成する必要があるということです。精神的に「コンパイラーをプレイ」して、特定の入力セットに何が起こるかをシミュレートするという意味だけでなく、より数学的な意味でコードを推論します。

たとえば、単体テストの目的は、「壊れる可能性があるものすべて」をテストすることです。あなたが投稿した最初のコード スニペットを見て、関数について推論し、「この関数はどのように壊れたのでしょうか?」と尋ねることができます。これは十分に単純な関数であるため、コンパイラをまったく使用する必要はありません。process()関数が特定の入力セットに対して正しい値を返さなかった場合、つまり、無効な結果を返した場合や例外をスローした場合、関数は壊れると言えます。これは、正しい値を に渡すために、正しい結果extractBar()を返すかどうかをテストする必要があることを意味します。extractBaz()process()

実際には、foo()が予期しない例外をスローするかどうかをテストするだけで済みます。それが行うのは callだけであり、独自の単体テスト セットでprocess()テストする必要があるためです。と もprocess()同じです。有効な入力が与えられたときにこれら 2 つの関数が正しい結果を返す場合、正しい値が に渡されます。また、有効な入力が与えられたときに正しい結果が生成される場合は、正しい結果も返されます。extractBar()extractBaz()process()process()foo()

data「引数はどうなるの? 構造体から間違った値を抽出したらどうなるの?」と言うかもしれません。しかし、それは本当に壊れますか?関数を見ると、コア JS ドット表記を使用してオブジェクトのプロパティにアクセスしています。アプリケーションの単体テストでは、言語自体のコア機能はテストしません。コードを見て、ハードコーディングされたオブジェクト プロパティ アクセスに基づいて値を抽出していることを判断し、他のテストに進みます。

これは、単体テストを捨てることができると言っているわけではありませんが、経験豊富な関数型プログラマーの多くは、壊れる可能性があるものだけをテストすればよく、関数型プログラミングによってテストの数が減るため、必要なテストがはるかに少ないことに気付きます。これにより、実際に危険にさらされている部分にテストを集中させることができます。

ところで、複雑なデータを扱っていて、FP を使用しても、考えられるすべての順列を推論するのが難しいのではないかと心配している場合は、生成テストを検討することをお勧めします。そのためのJSライブラリがいくつかあると思います。

于 2016-03-08T15:26:24.240 に答える