也许你之前已经用上了 useState 来做一些简单的状态管理,但是你现在需要一个全局的状态管理器来进行状态管理,此时就不能直接去 set 某个 state 的值啦。于是乎,useDispatch 就被拿来触发(dispatch)一个 action,然后再去 Redux store 改变 state 的值:
import userEvent from'@testing-library/user-event'import { createMemoryHistory } from'history'import { renderWithReduxAndRouter } from'../utils/testHelpers'import { categoryTabs } from'./CategoryTabs.stories'constmockDispatch=jest.fn()jest.mock('react-redux', () => ({useDispatch: () => mockDispatch,}))test('should show category list and fetch books by category', () => {consthistory=createMemoryHistory()const { queryByText } =renderWithReduxAndRouter(categoryTabs(), { history })userEvent.click(queryByText('文学'))expect(mockDispatch).toBeCalledWith({ type:'book/fetch', payload: { category:'文学', }, })expect(history.location.search).toBe('?category=文学')})test('should show "文学" category book when select "文学" tab', () => {})
本次任务的练习要求
redux-saga over redux-thunk(利于测试)
组件化拆分,不能有重复逻辑(通过 React Hook 提取)
实现 Redux 数据流,并保持 Outside-In TDD 的节奏
别忘了 Cypress E2E 测试还在运行着呢!
也请你思考一下
什么时机,回到 Cypress 检查页面元素是否正确显示?
describe('Home Page', () => {beforeEach(() => {cy.visit('/home') })it('should show book list by category and tag', () => {// todo: should update to testId from plain text?cy.contains('书架').click()cy.contains('Bookshelf')cy.contains('编程').click()cy.contains('Java编程思想')cy.contains('JavaScript 1本').click()cy.contains('Java编程思想')cy.contains('你不知道的JavaScript')cy.contains('文学').click()cy.contains('平凡的世界') })})
describe('Home page to fetch books API', () => {beforeEach(() => {cy.visit('/home') })constinitialBooks= [] // [...]constgetItems= () =>cy.request('/api/books').its('body')it('should fetch all books', () => {// before the request goes out we need to set up spying// see https://on.cypress.io/network-requestscy.server()cy.route('GET','/api/books').as('book')cy.wait('@book').should('have.property','status',200)cy.get('@book').its('response.body').should('deep.equal', { title:'example post', body:'this is a post sent to the server', userId:1, }) })})