欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

JavaScript中如何使用Mock模擬模塊并處理組件交互

本篇文章為大家展示了JavaScript中如何使用Mock模擬模塊并處理組件交互,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。

創(chuàng)新互聯(lián)是一家專注于成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作與策劃設(shè)計(jì),徐聞網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:徐聞等地區(qū)。徐聞做網(wǎng)站價(jià)格咨詢:028-86922220

我們將學(xué)習(xí)如何測(cè)試更復(fù)雜的組件,包括用 Mock 去編寫涉及外部 API 的測(cè)試,以及通過(guò) Enzyme 來(lái)輕松模擬組件交互

 

初次嘗試 Jest Mock

我們的應(yīng)用程序通常需要從外部的 API 獲取數(shù)據(jù)。在編寫測(cè)試時(shí),外部 API 可能由于各種原因而失敗。我們希望我們的測(cè)試是可靠和獨(dú)立的,而最常見(jiàn)的解決方案就是 Mock。

 

改寫 TodoList 組件

首先讓我們改造組件,使其能夠通過(guò) API 獲取數(shù)據(jù)。安裝 axios:

npm install axios
 

然后改寫 TodoList 組件如下:

// src/TodoList.js
import React, { Component } from 'react';
import axios from 'axios';

import Task from './Task';

const apiUrl = 'https://api.tuture.co';

class ToDoList extends Component {
  state = {
    tasks: [],
  };

  componentDidMount() {
    return axios
      .get(`${apiUrl}/tasks`)
      .then((tasksResponse) => {
        this.setState({ tasks: tasksResponse.data });
      })
      .catch((error) => console.log(error));
  }

  render() {
    return (
      <ul>
        {this.state.tasks.map((task) => (
          <Task key={task.id} id={task.id} name={task.name} />
        ))}
      </ul>
    );
  }
}

export default ToDoList;
 

TodoList 被改造成了一個(gè)“聰明組件”,在 componentDidMount 生命周期函數(shù)中通過(guò) axios 模塊異步獲取數(shù)據(jù)。

 

編寫 axios 模塊的 mock 文件

Jest 支持對(duì)整個(gè)模塊進(jìn)行 Mock,使得組件不會(huì)調(diào)用原始的模塊,而是調(diào)用我們預(yù)設(shè)的 Mock 模塊。按照官方推薦,我們創(chuàng)建 mocks目錄并把 mock 文件放到其中。創(chuàng)建 axios 的 Mock 文件 axios.js,代碼如下:

// src/__mocks__/axios.js
'use strict';

module.exports = {
  get: () => {
    return Promise.resolve({
      data: [
        {
          id: 0,
          name: 'Wash the dishes',
        },
        {
          id: 1,
          name: 'Make the bed',
        },
      ],
    });
  },
};
 

這里的 axios 模塊提供了一個(gè) get 函數(shù),并且會(huì)返回一個(gè) Promise,包含預(yù)先設(shè)定的假數(shù)據(jù)。

 

通過(guò) spyOn 函數(shù)檢查 Mock 模塊調(diào)用情況

讓我們開(kāi)始 Mock 起來(lái)!打開(kāi) TodoList 的測(cè)試文件,首先在最前面通過(guò) jest.mock 配置 axios 模塊的 Mock(確保要在 import TodoList 之前),在 Mock 之后,無(wú)論在測(cè)試還是組件中使用的都將是 Mock 版本的 axios。然后創(chuàng)建一個(gè)測(cè)試用例,檢查 Mock 模塊是否被正確調(diào)用。代碼如下:

// src/TodoList.test.js
import React from 'react';
import { shallow, mount } from 'enzyme';
import axios from 'axios';

jest.mock('axios');

import ToDoList from './ToDoList';

describe('ToDoList component', () => {
  // ...

  describe('when rendered', () => {
    it('should fetch a list of tasks', () => {
      const getSpy = jest.spyOn(axios, 'get');
      const toDoListInstance = shallow(<ToDoList />);
      expect(getSpy).toBeCalled();
    });
  });
});
 

測(cè)試模塊中一個(gè)函數(shù)是否被調(diào)用實(shí)際上是比較困難的,但是所幸 Jest 為我們提供了完整的支持。首先通過(guò) jest.spyOn,我們便可以監(jiān)聽(tīng)一個(gè)函數(shù)的使用情況,然后使用配套的 toBeCalled Matcher 來(lái)判斷該函數(shù)是否被調(diào)用。整體代碼十分簡(jiǎn)潔,同時(shí)也保持了很好的可讀性。

如果你忘記了 Jest Matcher 的含義,推薦閱讀本系列的第一篇教程。

 

迭代 TodoList 組件

一個(gè)實(shí)際的項(xiàng)目總會(huì)不斷迭代,當(dāng)然也包括我們的 TodoList 組件。對(duì)于一個(gè)待辦事項(xiàng)應(yīng)用來(lái)說(shuō),最重要的當(dāng)然便是添加新的待辦事項(xiàng)。

修改 TodoList 組件,代碼如下:

// src/TodoList.js
// ...
class ToDoList extends Component {
  state = {
    tasks: [],
    newTask: '',
  };

  componentDidMount() {
    // ...
      .catch((error) => console.log(error));
  }

  addATask = () => {
    const { newTask, tasks } = this.state;

    if (newTask) {
      return axios
        .post(`${apiUrl}/tasks`, { task: newTask })
        .then((taskResponse) => {
          const newTasksArray = [...tasks];
          newTasksArray.push(taskResponse.data.task);
          this.setState({ tasks: newTasksArray, newTask: '' });
        })
        .catch((error) => console.log(error));
    }
  };

  handleInputChange = (event) => {
    this.setState({ newTask: event.target.value });
  };

  render() {
    const { newTask } = this.state;
    return (
      <div>
        <h2>ToDoList</h2>
        <input onChange={this.handleInputChange} value={newTask} />
        <button onClick={this.addATask}>Add a task</button>
        <ul>
          {this.state.tasks.map((task) => (
            <Task key={task.id} id={task.id} name={task.name} />
          ))}
        </ul>
      </div>
    );
  }
}

export default ToDoList;
 

由于我們大幅改動(dòng)了 TodoList 組件,我們需要更新快照:

npm test -- -u
 

如果你不熟悉 Jest 快照測(cè)試,請(qǐng)回看本系列第二篇教程。

更新后的快照文件反映了我們剛剛做的變化:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ToDoList component when provided with an array of tasks should render correctly 1`] = `
<div>
 <h2>
   ToDoList
 </h2>
 <input
   onChange={[Function]}
   value=""
 />
 <button
   onClick={[Function]}
 >
   Add a task
 </button>
 <ul />
</div>
`;
   

在測(cè)試中模擬 React 組件的交互

在上面迭代的 TodoList 中,我們使用了 axios.post。這意味著我們需要擴(kuò)展 axios 的 mock 文件:

// src/__mocks__/axios.js
'use strict';

let currentId = 2;

module.exports = {
  get: () => {
    return Promise.resolve({
      // ...
      ],
    });
  },
  post: (url, data) => {
    return Promise.resolve({
      data: {
        task: {
          name: data.task,
          id: currentId++,
        },
      },
    });
  },
};
 

可以看到上面,我們添加了一個(gè) currentId 變量,因?yàn)槲覀冃枰3置總€(gè) task 的唯一性。

讓我們開(kāi)始測(cè)試吧!我們測(cè)試的第一件事是檢查修改輸入值是否更改了我們的狀態(tài):

我們修改 app/components/TodoList.test.js 如下:

import React from 'react';
import { shallow } from 'enzyme';
import ToDoList from './ToDoList';

describe('ToDoList component', () => {
  describe('when the value of its input is changed', () => {
    it('its state should be changed', () => {
      const toDoListInstance = shallow(
        <ToDoList/>
      );

      const newTask = 'new task name';
      const taskInput = toDoListInstance.find('input');
      taskInput.simulate('change', { target: { value: newTask }});

      expect(toDoListInstance.state().newTask).toEqual(newTask);
    });
  });
});
 

這里要重點(diǎn)指出的就是 simulate[1] 函數(shù)的調(diào)用。這是我們幾次提到的ShallowWrapper的功能。我們用它來(lái)模擬事件。它第一個(gè)參數(shù)是事件的類型(由于我們?cè)谳斎胫惺褂胦nChange,因此我們應(yīng)該在此處使用change),第二個(gè)參數(shù)是模擬事件對(duì)象(event)。

為了進(jìn)一步說(shuō)明問(wèn)題,讓我們測(cè)試一下用戶單擊按鈕后是否從我們的組件發(fā)送了實(shí)際的 post 請(qǐng)求。我們修改測(cè)試代碼如下:

import React from 'react';
import { shallow } from 'enzyme';
import ToDoList from './ToDoList';
import axios from 'axios';

jest.mock('axios');

describe('ToDoList component', () => {
  describe('when the button is clicked with the input filled out', () => {
    it('a post request should be made', () => {
      const toDoListInstance = shallow(
        <ToDoList/>
      );
      const postSpy = jest.spyOn(axios, 'post');

      const newTask = 'new task name';
      const taskInput = toDoListInstance.find('input');
      taskInput.simulate('change', { target: { value: newTask }});

      const button = toDoListInstance.find('button');
      button.simulate('click');

      expect(postSpy).toBeCalled();
    });
  });
});
 

感謝我們的 mock 和 simulate 事件,測(cè)試通過(guò)了!現(xiàn)在事情會(huì)變得有些棘手。我們將測(cè)試狀態(tài)是否隨著我們的新任務(wù)而更新,其中比較有趣的是請(qǐng)求是異步的,我們繼續(xù)修改代碼如下:

import React from 'react';
import { shallow } from 'enzyme';
import ToDoList from './ToDoList';
import axios from 'axios';

jest.mock('axios');

describe('ToDoList component', () => {
  describe('when the button is clicked with the input filled out, the new task should be added to the state', () => {
    it('a post request should be made', () => {
      const toDoListInstance = shallow(
        <ToDoList/>
      );
      const postSpy = jest.spyOn(axios, 'post');

      const newTask = 'new task name';
      const taskInput = toDoListInstance.find('input');
      taskInput.simulate('change', { target: { value: newTask }});

      const button = toDoListInstance.find('button');
      button.simulate('click');

      const postPromise = postSpy.mock.results.pop().value;

      return postPromise.then((postResponse) => {
        const currentState = toDoListInstance.state();
        expect(currentState.tasks.includes((postResponse.data.task))).toBe(true);
      })
    });
  });
});
 

就像上面看到的,postSpy.mock.results 是 post 函數(shù)發(fā)送結(jié)果的數(shù)組,通過(guò)使用它,我們可以得到返回的 promise,我們可以從 value 屬性中取到這個(gè) promise。從測(cè)試返回 promise 是確保 Jest 等待其異步方法執(zhí)行結(jié)束的一種方法。

 

小結(jié)

在本文中,我們介紹了 mock 模塊,并將其用于偽造API調(diào)用。由于沒(méi)有發(fā)起實(shí)際的 post 請(qǐng)求,我們的測(cè)試可以更可靠,更快。除此之外,我們還在整個(gè) React 組件中模擬了事件。我們檢查了它是否產(chǎn)生了預(yù)期的結(jié)果,例如組件的請(qǐng)求或狀態(tài)變化。為此,我們了解了 spy 的概念。

 

嘗試測(cè)試 React Hooks

Hooks 是 React 的一個(gè)令人興奮的補(bǔ)充,毫無(wú)疑問(wèn),它可以幫助我們將邏輯與模板分離。這樣做使上述邏輯更具可測(cè)試性。不幸的是,測(cè)試鉤子并沒(méi)有那么簡(jiǎn)單。在本文中,我們研究了如何使用 react-hooks-testing-library[2] 處理它。

我們創(chuàng)建 src/useModalManagement.js 文件如下:

// src/useModalManagement.js
import { useState } from 'react';

function useModalManagement() {
  const [isModalOpened, setModalVisibility] = useState(false);

  function openModal() {
    setModalVisibility(true);
  }

  function closeModal() {
    setModalVisibility(false);
  }

  return {
    isModalOpened,
    openModal,
    closeModal,
  };
}

export default useModalManagement;
 

上面的 Hooks 可以輕松地管理模式狀態(tài)。讓我們開(kāi)始測(cè)試它是否不會(huì)引發(fā)任何錯(cuò)誤,我們創(chuàng)建 useModalManagement.test.js

// src/useModalManagement.test.js
import useModalManagement from './useModalManagement';

describe('The useModalManagement hook', () => {
  it('should not throw an error', () => {
    useModalManagement();
  });
});
 

我們運(yùn)行測(cè)試,得到如下的結(jié)果:

FAIL useModalManagement.test.js
  The useModalManagement hook
    ? should not throw an error按 ?+? 退出
 

不幸的是,上述測(cè)試無(wú)法正常進(jìn)行。我們可以通過(guò)閱讀錯(cuò)誤消息找出原因:

無(wú)效的 Hooks 調(diào)用, Hooks 只能在函數(shù)式組件的函數(shù)體內(nèi)部調(diào)用。

 

讓測(cè)試通過(guò)

React文檔[3] 里面提到:我們只能從函數(shù)式組件或其他 Hooks 中調(diào)用 Hooks。我們可以使用本系列前面部分介紹的 enzyme 庫(kù)來(lái)解決此問(wèn)題,而且使了一點(diǎn)小聰明,我們創(chuàng)建 testHook.js

// src/testHook.js
import React from 'react';
import { shallow } from 'enzyme';

function testHook(hook) {
  let output;
  function HookWrapper() {
    output = hook();
    return <></>;
  }
  shallow(<HookWrapper />);
  return output;
}

export default testHook;
 

我們繼續(xù)迭代 useModalManagement.test.js,修改內(nèi)容如下:

// src/useModalManagement.test.js
import useModalManagement from './useModalManagement';
import testHook from './testHook';

describe('The useModalManagement hook', () => {
  it('should not throw an error', () => {
    testHook(useModalManagement);
  });
});
 

我們?cè)试S測(cè)試,得到如下結(jié)果:

PASS useModalManagement.test.js
  The useModalManagement hook
    ? should not throw an error
 

好多了!但是,上述解決方案不是很好,并且不能為我們提供進(jìn)一步測(cè)試 Hooks 的舒適方法。這就是我們使用  react-hooks-testing-library[4]   的原因。

上述內(nèi)容就是JavaScript中如何使用Mock模擬模塊并處理組件交互,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

分享標(biāo)題:JavaScript中如何使用Mock模擬模塊并處理組件交互
標(biāo)題URL:http://www.chinadenli.net/article24/iiicje.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App開(kāi)發(fā)網(wǎng)站內(nèi)鏈網(wǎng)站設(shè)計(jì)公司品牌網(wǎng)站制作微信小程序響應(yīng)式網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名