反応成分模擬の基本形


私は、なぜmockingが役に立つかについて見ました.
この部分では、私は反応の模擬コンポーネントの基本的な形式をカバーします.
このポストのすべてのコードサンプルは、以下のレポで利用できます.

dirv / コンポーネントをmocking反応


コンポーネントをモックアップする方法の例


作業中のコンポーネントをもう一度見てみましょう.BlogPage and PostContent .
こちらですBlogPage :
const getPostIdFromUrl = url =>
  url.substr(url.lastIndexOf("/") + 1)

export const BlogPage = ({ url }) => {

  const id = getPostIdFromUrl(url)

  return (
    <PostContent id={id} />
  )
}
BlogPage ショー以外のことはしないPostContent . しかし、それは我々が興味を持っている機能の小さな部分を持っていますurl prop値必須の投稿を引き出すid .PostContent もう少し複雑になっています.ブラウザの内蔵を呼び出しますfetch ブログ投稿のテキストをURLで取得する関数/post?id=${id} , どこid 支柱はそれに渡されます.
export const PostContent = ({ id }) => {
  const [ text, setText ] = useState("")

  useEffect(() => {
    fetchPostContent(id)
  }, [id])

  const fetchPostContent = async () => {
    const result = await fetch(`/post?id=${id}`)
    if (result.ok) {
      setText(await result.text())
    }
  }

  return <p>{text}</p>
}
実際、何PostContent 我々は再びそれを見に行かないので、それは重要ではありませんか!
我々はいくつかのテストを書くつもりですBlogPage テストファイルでBlogPage.test.js . それをするには、我々はモックアウトするPostContent そのため、実装を心配する必要はありません.
重要な点は、我々はスタブアウトですPostContent それで我々BlogPage.test.js テストスイートはそれが何であるかから遮蔽されるPostContent です.
これがモックですPostContent :
import { PostContent } from "../src/PostContent"

jest.mock("../src/PostContent", () => ({
  PostContent: jest.fn(() => (
    <div data-testid="PostContent" />
  ))
}))
これを壊しましょう.
  • モックはjest.mock . これは対応するインポートをミラーする必要があります.そのように呼び出しを上げますimport 置換できます.Jestはモジュール全体を新しく定義したモジュールに置き換えます.だからこの場合、我々は全体を嘲笑している../src/PostContent ファイル.
  • MOCKがモジュールレベルであるので、あなたがモックしているどんなコンポーネントもそれ自身のモジュールである必要があります.
  • への呼び出しjest.fn スパイを生成します:それが呼ばれるとき、そして、どんなパラメタで記録するオブジェクト.次に、toHaveBeenCalled and toHaveBeenCalledWith マッチャー
  • パラメータjest.fn 関数が呼び出されたときに返されるスタブ値を定義します.
  • スタブの実装は常にそれらを作ることができるように単純である必要があります.反応するコンポーネントのためにdiv —これは間違いなくHTML要素の意味の最小量です!
  • の属性がありますdata-testid この特定の要素をDOMに保持するために使用します.
  • 使用試験に反対する反応テストライブラリdata-testid テストランナーがあなたのソフトウェアを使っている本当の人であるかのように、あなたがあなたのテストを扱うことを望むので、可能であるところ.しかし、モックのために、私はその指導を無視します.
  • The data-testid valueコンポーネントの名前にマッチします.この場合、それはPostContent . これは私が私のすべての塊のために続く標準的な慣例です.
  • これは、反応成分の塊の基本的な形です.私のおかあさんの90 %(またはそれ以上)は、これを見ます.他の10 %は、後のポストで見る若干の小さい追加をします.
    そのモックーで、いくつかのテストを書きましょうBlogPage .

    モンクされたコンポーネントがDOMでレンダリングされることを確認する


    describe("BlogPage", () => {
      it("renders a PostContent", () => {
        render(<BlogPage url="http://example.com/blog/my-web-page" />)
        expect(screen.queryByTestId("PostContent"))
          .toBeInTheDocument()
      })
    })
    
    このテストは、コンポーネントのMCKを使用するときに常に必要な2つのテストの最初です.The screen.queryByTestId 現在のDOM内のコンポーネントを検索するdata-testidPostContent .
    言い換えれば、我々は実際に実際にレンダリングしたことを確認しますPostContent コンポーネント.

    QuerybyStatdの責任ある使用法


    私が使った通知queryByTestId . 反応テストライブラリは、2つのアカウントでこの機能からあなたをプッシュしようとします:まず、それはあなたが使用したいgetBy 賛成にqueryBy , そして第二に、既に述べたように、テストIDで検索したくない.
    実際、テストの模擬は私が使用する唯一の時間ですqueryByTestId . 私は、私が使用を避けることができなかった時間を考えることができませんTestId 非モンク部品のバリアントしかし、それが正確にその技術的な詳細は、我々はチェックしたいのです.ユーザーはこのコンポーネントを見ることはありません、それは純粋に我々のテストのためです.
    我々が得るのは、モックオブジェクトを構築する一貫した方法を持つ能力です.<div data-testid="ComponentName" /> すべてのmockオブジェクトに使用できる標準パターンです.

    Getby *対Queryby

    getBy variantは要素にマッチできない場合に例外を送出します.私の意見では、呼び出しが予想の一部でないとき、これは適切なだけです.
    ですから、
    expect(screen.getByTestId("PostContent"))
      .toBeInTheDocument()
    
    あなたがレンダリングしなかったならば<PostContent /> このテストは1からの例外で爆発するだろうgetByTestId . 期待は全く実行されません!
    期待が失敗し、例外が発生するかどうかの選択を考えてみると、テストランナーにとって意味があるので、いつか期待を選べます.

    Unit tests, and in particular when TDD style tests, are very often about the presence of elements. For these tests I find the queryBy much more to my liking.


    模擬が正しい小道具を通過することを確かめる


    番目のテストでは、右小道具が渡されたチェックが必要ですPostContent .
    it("constructs a PostContent with an id prop created from the url", () => {
      const postId = "my-amazing-post"
      render(<BlogPage url={`http://example.com/blog/${postId}`} />)
      expect(PostContent).toHaveBeenCalledWith(
        { id: postId },
        expect.anything())
    })
    
    これは標準的なjest matchersを使用します.toHaveBeenCalledWith それを保証するPostContent 関数は、期待しているパラメータで呼び出されました.
    反応があなたの構成要素をインスタンス化するとき、それは単に最初のパラメタとしてのプロップと第2のパラメタとしてrefを持つ定義された機能を呼び出しています.番目のパラメータは通常重要ではありません.
    JSXステートメント<PostContent id="my-amazing-post" /> 関数コールの結果PostContent({ id: "my-amazing-post" }) .
    しかし、それは我々にとって有用でない幻の第2のパラメータも含みます.

    期待を使う2番目のパラメータに対しては何かを指定します。


    反応があなたの構成要素に渡す第2のパラメタは、インスタンスRefです.それは通常我々のテストに重要でありませんexpect.anything() あなたがその価値に興味がないことを意味します.
    あなたが取り除くことを望むならばexpect.anything() コールは、あなたのためにそれを渡す独自のジェスマッチを書くことができます.

    プロップを渡していない場合は、


    まれに、モックオフしたコンポーネントは、パラメータを取ることはありません.使えますtoHaveBeenCalled の簡単なバージョンとしてtoHaveBeenCalledWith .

    部品塊の基本ルールの理解


    つのテストと1つの模擬を書きました.ここで私たちがこれまで明らかにした重要な教訓があります.
  • あなたのモックはスパイを使用する必要がありますjest.fn また、可能な限り単純なコンポーネントの返り値を持ちます.<div />
  • また、data-testid 属性を使用すると、DOM内の要素を直接指定できます.
  • この属性の値は、慣習化されたコンポーネントの名前です.だからPostContent コンポーネントの値は<div data-testid="PostContent" /> .
  • すべての模擬は少なくとも2つのテストを必要とします:最初のチェックはDOMに存在し、2番目のテストは正しい小道具で呼び出されます.
  • なぜ2つのテスト?


    私は少なくとも2つのテストを必要とする2、3回言及しました.しかし、なぜですか?
    最初のテストを行わなかった場合、DOMの存在を確認するには、単純な関数呼び出しを使用して2番目のテストパスを作成できます.
    export const BlogPost = () => {
      PostContent({ id: "my-awesome-post" })
      return null
    }
    
    なぜ、あなたがこれをしたいと思うかは、他の全部のブログ柱の主題です、しかし、ここでは、短いバージョンです:一般に、我々はJSX声明より単純である関数呼び出しを考慮します.厳密なテスト原理を使用しているときは、常にテストパスを作成するために最も単純なコードを記述する必要があります.
    さて、あなたが最初のテストをしたが、2番目ではない場合はどうですか?
    次のようにしてください.
    export const BlogPost = () => (
      <PostContent />
    )
    
    繰り返しますが、これはテストパスを作る最も簡単な生産コードです.
    実際のソリューションを取得するには、両方のテストが必要です.
    これは、エンドツーエンドテストと単体テストの重要な違いです:ユニットテストはエンドツーエンドテストがない傾向にある方法で防御です.
    キーポイント:常にあなたのテストを渡すために最も簡単な生産コードを書く.そうすることで、すべてのシナリオをカバーするテストスイートを書くのに役立ちます.
    それは模擬コンポーネントの基礎をカバーします.次の部分で、我々は見ます.