shunnami
@shunnami (miffiy)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Reactコンポーネント内の一部の関数をモック化してテストしたいです

Q&A

Closed

解決したいこと

【テスト環境】

Jest・React Testing Library

【困っていること】

UIコンポーネント内で動作する一部の関数を、テストをするために好きな値を返す関数に置き換えたいのですが、やり方がわかりません。

【アプリケーションの動作説明】※実際のソースコードは記事の一番したに記載しております

はじめに、App.tsxで、const [dispFlg,setDispFlg] = useState(false)が初期化されます。これは、下記の関数が成功したときにtrueに切り替わることで、画面に成功の文字を出すためのフラグです。
次に、「Function-start」ボタンを押下します。このとき、handleClick関数が動き「getUserName()」が動きます。

getUserNameでは、まず初めにapiResult変数を初期化します。apiResultは「getUserName()」がreturnする値になります。
次にaxiosによってHTTP通信を行い、外部からuser情報を持ったHTTPレスポンスを受け取ります。
レスポンスはresponse関数に格納します。そのあとは、HTTPステータスによって条件分岐をします。
HTTPステータス200なら、「getUserName()」の戻り値である、apiResultへ値を詰め、returnします。

App.tsxに戻り、getUserNameの戻り値をresult変数に格納します。この後の処理はresultをつかって条件分岐を行います。
apiResultでHTTPステータスが200だったならresult.resultにtrueが入っているので
setDispFlg(true)として、画面に成功の文字を表示します

【やりたいこと】

ボタン押下後に走る「getUserName()」を、好きな値を返すような関数に置き換えたいです。
置き換えることで後続の処理の条件分岐を操り、画面に「成功」の文字の出るのか・出ないのかをテストしたいです。

例えば、getUserName()を以下のtest関数に置き換えることは可能でしょうか。

const test = () => {
  const apiResult = {
    result:true,
    userName: "",
  }
  // apiResultをPromiseかつaxiosの型にキャスト
  return apiResult;
}

該当するソースコード

/**
* App.tsx
*/
import {getUserName} from './getUserName';
import { useState } from 'react';

export default function App() {
  const [dispFlg,setDispFlg] = useState(false)
  const handleClick = async () => {
    const result = await getUserName();
    if(result?.Result){
      console.log("成功:ユーザ名は=>",result.userName)
      setDispFlg(true);
    } else {
      console.log("失敗")
    }
  };

  return (
    <div>
      <button onClick={handleClick}>Function-Start</button>
      {
        dispFlg && <p>成功</p>
      }
    </div>
  );
}

/**
* getUserName.tsx
*/
import baseApi from "./baseApi"
export const getUserName  = async () => {
  const apiResult = {
    result:false,
    userName: "",
  }
  try{
  const response = await baseApi.get("/users");
  console.log(response);
  if (response.status === 200) {
    apiResult.result = true;
    apiResult.userName = response.data[0].name;
  }else{
    apiResult.result = false
  }
  console.log(apiResult);
  return apiResult;
  } catch (error) {
  console.log(error.message);
  }
}
/**
* baseApi.tsx
*/
import axios from 'axios';

export const baseApi = axios.create({
  baseURL: '"https://jsonplaceholder.typicode.com/',
});
/**
* ボタンをクリックしたときのコンソールログ
* 
* {data: Array(10), status: 200, statusText: "", headers: {…}, config: {…}, …}
* getUserName.ts:20 {Result: true, userName: "Leanne Graham"}
* App.tsx:7 成功:ユーザ名は=> Leanne Graham
*/

0

3Answer

Comments

  1. @shunnami

    Questioner

    ご回答ありがとうございます。

    ご教示いただいたJest公式を学習してみたのですが、
    私がやりたいことはできないように思いました。

    理由としては、getUserName()のことをモック(マニュアルモック)にはできるのですが、
    handleClick 関数内でモックとして扱うことはできなそうだと思ったからです。

    Jestの公式記事を貼っていただいたのは、「あなたのやりたいことはできませんよ」
    ということなのでしょうか。

    引き続き勉強は続けていきたいと思いますが、このテストはできなそうなので、
    諦めようと思います。

    ありがとうございました。

結構バッチリなドキュメントと思ったんですが。

getUserNameって要はaxiosの結果次第で結果が変わるわけですよね?

const apiResult = {
  result:true,
  userName: "",
}

こういう結果を常に返したいんだったら、

  • response.status == 200
  • response.data[0].name == ""

になるようなaxiosのresponseが返ってくればいいようにmockすればいいわけで... リンクのドキュメントはモロにそれをやってると思います。もう一回見直してみてください。

1Like

Comments

  1. @shunnami

    Questioner

    ご回答ありがとうございます。
    仰る通りバッチリな内容でしたが、私の飲み込みが悪くすみません。

    モックにすることができました。

    しかし、urlの共通部分をaxios.createメソッドを利用して省略しようとしたところ、エラーが出てしまいました。

    おそらく、createメソッドをモックすれば解決しそうというところまではわかっているのですが、現状では、製造が止まっている状況です。

    ご存知でしたら、axios.createメソッドを使った場合のmock方法をご教示いただけないでしょうか。


    ```ts
    /**
    * baseApi.tsx
    */
    import axios from 'axios';

    export const baseApi = axios.create({
    baseURL: '"https://jsonplaceholder.typicode.com/',
    });
    ```

コメントだとmarkdownが一切効かないので...

多分そういう場合だと

  1. まずはbaseApiをテストする(mockするなりなんなり)
  2. baseApiを使う他のファイルはbaseApiはテスト済みなので、想定するものを返すmockをする

今回の場合なら

  • baseApiのテストファイル
  • getUserNameのテストファイル(baseApiをmock)
  • Appのテストファイル(getUserNameをmock)

と段階的にテストしていれば、Appの時にbaseApiとgetUserNameをmockするとかの複雑なことをしなくて良くなる。

baseApiテスト済みで、getUserNameのテストファイルであれば、

  1. baseApiをmockする
  2. baseApiはaxiosのオブジェクトを返す
  3. そのオブジェクトはgetをmockしておく

というような手順で考えれば良いのかなと思います。

今回の場合だとaxiosのgetしか使ってないので

  1. baseApiをmockする
  2. baseApiは.getをもつmockを返す
  3. .getはresponseを返す

というような流れでも良いかなと思います。

因みにタグ<...>が入っていないファイルなら.tsxは.tsの方が良いです

  • xはXMLの意味
  • JavaScript XML -> jsx
  • TypeScript XML -> tsx
1Like

Comments

  1. @shunnami

    Questioner

    ご回答ありがとうございます。

    ご丁寧かつ詳細な解説を記載していただきありがとうございます。

    mockできなかったのは、私の勘違いからでした。
    また、テストのやりかたなど大変勉強になりました。

    ありがとうございました。

Your answer might help someone💌