Reactエコシステムについて私が気に入っているのは、多くのIDEASが多くの決定の背後にあるということです。 さまざまな著者が既存の順序をサポートするさまざまな記事を書いて、すべてが「正しい」理由を説明するので、誰もがパーティーが正しい軌道に乗っていることを理解しています。
しばらくして、IDEAは少し変わり、すべてが最初から始まります。
そして、この話の始まりは、コンポーネントをコンテナと非コンテナに分離することです(一般的にダムコンポーネントと呼ばれます。私のフランス語は申し訳ありません)。

問題
問題は非常に単純です-単体テスト。 最近、統合テストに向けた動きがいくつかあります- 「テストを書いてください。多すぎず、ほとんどが統合です。」 。 これは悪い考えではなく、時間が短い場合(およびテストが特に必要ない場合)、これを行う必要があります。 それを煙テストと呼びましょう-何も爆発しないように確認します。
時間がかかり、テストが必要な場合、適切な統合テストを書くのは非常に長いので、このようにしない方が良いです。 成長していくという理由だけで、右側の3番目のボタンをテストするには、メニューの3つのボタンをクリックする必要があります。ログインすることを忘れないでください。 一般的に-銀の大皿での組み合わせの爆発です。
ここでの解決策は、1つの簡単な(定義による)単体テストです。 アプリケーションの一部の既製の状態でテストを開始する機能。 より正確には、アプリケーションまたはビッグブロックからテストエリアを小さなもの(ユニットなど)に縮小(狭小化)することです。 酵素を使用する必要はありません-魂が尋ねるなら、ブラウザテストを実行できます。 ここで最も重要なことは、 単独で何かをテストできることです。 そして、あまり手間がかかりません。
分離は単体テストの重要なポイントの1つであり、それが単体テストが気に入らない理由です。 彼らはさまざまな理由でそれを好まない:
- たとえば、「ユニット」はアプリケーションと離婚し、独自のテストがグリーンであっても、その構成では機能しません。
- または、例えば、孤立は、誰も見たことがないような真空中の球形の馬であるためです。 それを達成する方法と測定する方法は?
個人的には、ここで問題は発生していません。 もちろん、最初の段落では、統合テストを推奨できます。統合テストは、そのために考案されたもので、事前にテストされたコンポーネントが正しく組み立てられる方法を確認します。 もちろん、アプリケーションの一部としてではなく、自分自身だけをテストするnpmパッケージを信頼します。 「コンポーネント」と「あなた以外」のパッケージとの違いは何ですか?
2番目の段落では、すべてが少し複雑です。 そして、この記事が(およびそれ以前のすべてが-はじめに)-「ユニット」 ユニットをテスト可能にする方法についてです。
分割統治
Reactコンポーネントを「コンテナ」と「プレゼンテーション」に分割するという考え方は新しいものではなく、十分に説明されており、すでに少し時代遅れになっています。 Dan Abramovの記事を基礎(開発者の99%が行うこと)にすると、プレゼンテーションコンポーネント:
- 物事がどのように見えるかに関心がある
- 内部にプレゼンテーションコンポーネントとコンテナコンポーネントの両方が含まれる場合があります
**
通常、独自のDOMマークアップとスタイルがあります) - サポートスロット(多くの場合、this.props.childrenによる封じ込めを許可します)
- アプリケーションに依存しない(Fluxアクションやストアなど、アプリの他の部分に依存しない)
- データに依存しないでください(データのロード方法または変更方法を指定しないでください)
- インターフェイスは小道具に基づいています(小道具を介して排他的にデータとコールバックを受信します)
- 多くの場合、ステートレス(独自の状態を持つことはまれです(存在する場合、データではなくUI状態です))
- 多くの場合、SFC(状態、ライフサイクルフック、またはパフォーマンスの最適化が必要でない限り、機能コンポーネントとして記述されます)
コンテナはすべてロジックであり、すべてのデータへのアクセスであり、原則としてアプリケーション全体です。
理想的な世界では、コンテナはトランクであり、プレゼンテーションコンポーネントはリーフです。
ダンの定義には2つの重要なポイントがあります。それは、ほとんどが学術的な「ユニット」の定義である「アプリケーション非依存」と 、「これらの星が特に興味深い場合、他のプレゼンテーションコンポーネントとコンテナの両方を含めることができる**
」*です。
(無料翻訳)**私の記事の初期のバージョンで、私(Dan)は、プレゼンテーションコンポーネントには他のプレゼンテーションコンポーネントのみを含めるべきだと述べました。 私はもうそうは思いません。 コンポーネントのタイプは詳細であり、時間とともに変化する可能性があります。 一般に、それを共有しないでください。すべては大丈夫です。
この後何が起こるかを思い出しましょう:
- ストーリーブックでは、左側の3番目のボタンのある種のコンテナが、そこにない側にいているため、すべてが落ちます。 graphql、react-router、および他のreact-intlへの特別な挨拶。
- テストでマウントを使用する機能は失われます。これは、AからZまでのすべてをレンダリングするためです。また、レンダーツリーの深さのどこかで誰かが何かを実行すると、テストが失敗します。
- (具象的に)セレクター/リゾルバーを(特にproxyquireで)ウェットにする機能が失われ、アプリケーション全体の状態をウェットにする必要があるため、アプリケーションの状態を制御する機能が失われます。 そして、これは単体テストに適しています。
問題が少し不自然だと思う場合-非コンテナで使用されるこれらのコンテナが他の部門で変更され、その結果、あなたと彼らがテストを見たときにチームとして働いてみてください。それはうまくいきました、そして今再び。
その結果、浅いものを使用する必要があります。これにより、設計上 、有害な(および予期しない)副作用がすべて排除されます。 これは記事「なぜ私はいつも浅いを使うのか」からの簡単な例です
ツールチップが「?」をレンダリングすると想像してください。クリックすると、タイプ自体が表示されます。
import Tooltip from 'react-cool-tooltip'; const MyComponent = () => { <Tooltip> hint: {veryImportantTextYouHaveToTest} </Tooltip> }
それをテストするには? マウント+クリック+表示されているものを確認します。 これは統合テストであり、ユニットではありません。質問は、「エイリアン」コンポーネントをクリックする方法です。 浅瀬には問題がありません。 脳も「エイリアンコンポーネント」自体もないからです。 しかし、ツールチップはコンテナであり、MyComponentは実質的にプレゼンテーションであるため、ここには頭脳があります。
jest.mock('react-cool-tooltip', {default: ({children}) => childlren});
ただし、react-cool-tooltipを使用すれば、テストに問題はありません。 「コンポーネント」は、非常に鈍く、はるかに短く、はるかに有限になりました 。
最終コンポーネント
- 既知のサイズのコンポーネント。これには、他の既知の最終コンポーネントが含まれるか、まったく含まれない場合があります。
- 制御されていない状態と「増加」サイズを含むため、他のコンテナは含まれません。 現在のコンポーネントを無限にします。
- それ以外の場合は、通常のプレゼンテーションコンポーネントです。 実際、ダンの記事の最初のバージョンで説明されているとおりです。
最後のコンポーネントは、大きなメカニズムから取り出されたギアです。
全体の問題は、それをどのように取り出すかです。
解決策1-DI
私のお気に入りは依存性注入です。 ダンも彼を愛しています 。 一般に、これはDIではなく「スロット」です。 簡単に言えば、プレゼンテーション内でコンテナを使用する必要はありません-そこに注入する必要があります。 そして、テストでは、何か他のものを注入することが可能になります。
これは、 「コンテナがトランクであり、プレゼンテーションコンポーネントがリーフである」場合です。
解決策2-境界
多くの場合、DIはクールです。 おそらく今、%username%は現在のコードベースにどのように適用できるかを考えており、解決策は発明されていません...
そのような場合、 Bordersはあなたを救います。
const Boundary = ({children}) => ( process.env.NODE_ENV === 'test' ? null : children
ここでは、「スロット」の代わりに、単にすべての「移行ポイント」が境界に変わり、テスト中に何でもレンダリングします。 宣言的に十分で、まさに「ギアを取り出す」ために必要なもの。
解決策3-層
境界線は少し粗い場合があり、レイヤーに関する知識を少し追加することで、境界線を少しスマートにする方が簡単かもしれません。
const checkTier = tier => tier === currentTier; const withTier = tier => WrapperComponent => (props) => ( (process.env.NODE_ENV !== 'test' || checkTier(tier)) && <WrapperComponent{...props} /> ); const PageChrome = () => ( <section> <aside><ASideContainer /></aside> <Page /> </section> ); const ASideContainer = withTier('UI')(...) const Page = withTier('Page')(...) const PageChromeContainer = withTier('UI')(PageChrome);
ティア/レイヤーという名前の下では、機能、アヒル、モジュール、またはそのレイヤー/ティアだけが異なることがあります。 ポイントは重要ではありません。主なことは、ギアを引くことができることです。おそらく1つではなく、最終的な数字で、必要なものと不要なものを何らかの形で線引きします(異なるテストではこれは異なる境界線です)。
そして、これらの境界を何らかの形でマークすることを妨げるものは何もありません。
解決策4-個別の懸念
解決策が(定義により)エンティティの分離にある場合-エンティティを取得して分離するとどうなりますか?
私たちがとても嫌いな「コンテナ」は、一般にコンテナと呼ばれます。 そうでない場合は、今すぐにコンポーネントの名前を何とか音を立て始めます。 または、名前に特定のパターンがあります-接続(WrappedComonent)、またはGraphQL /クエリ。
実行時に、名前に基づいてエンティティ間に線を引くとどうなりますか?
const PageChrome = () => ( <section> <aside><ASideContainer /></aside> <Page /> </section> );
さらに、テストの1行、 react-remockは 、テストに干渉する可能性のあるすべてのコンテナーを削除します。
原則として、このアプローチはコンテナ自体をテストするために使用できます-最初のコンテナ以外のすべてを削除するだけです。
import {createElement, remock} from 'react-remock';
再び-いくつかのラインとギアが削除されました。
合計
過去1年間、Reactコンポーネントのテストは、特にマウントの場合、より複雑になりました。10個のプロバイダー、コンテキストすべてを上書きする必要があり、適切なスタイルで適切なコンポーネントをテストすることがますます難しくなっています。
誰かが唾を吐き、浅い世界に入ります。 誰かが単体テストで手を振って、すべてをサイプレスに転送しました(歩くような歩行!)。
他の誰かが反応に指を突っ込んで、これらは代数的な効果であり、あなたが望むことは何でもできると言います。 上記の例はすべて、本質的にこれらの代数効果とモックの使用です。 私とDIにとって、これらはmokiです。
PS:この投稿は、Reactチームがすべてを壊したという事実についてのReact / RFCのコメントへの応答として書かれており、 そこにあるすべてのポリマーもそうです。
PPS:この投稿は実際には別の非常に無料の翻訳です
PPPS:一般的に、本当の分離のために、 rewiremockを見てください