3

我有一个搜索过滤器组件,里面有一个输入,它连接到 redux。

class SearchFilter extends PureComponent {
   constructor (props) {
       super(props);
       this.handleInputChange = this.handleInputChange.bind(this);
       this.state = {
           keyword: props.searchKeyword
       };
   }

   handleInputChange (e) {
       this.setState({keyword: e.target.value});
       this.props.dispatch(SetFiltersValue(...);
   }

   render () {
       const {keyword} = this.state;
       return (
           <div className="search-w bp3-input-group">
               <span className="bp3-icon bp3-icon-search"/>
               <input className="bp3-input" value={keyword} type="text" placeholder="Search input" dir="auto" onChange={this.handleInputChange} />
           </div>
       );
   }
}

const mapStateToProps = (_, ownParams) => {...};
export default connect(mapStateToProps)(SearchFilter);

我用 jest 和酵素写了一个测试来测试用户输入新值时的状态更新

import React from "react";
import SearchFilter from "../../dev/components/shared/searchFilter";
import {shallow, mount} from "enzyme";
import configureStore from "redux-mock-store";

describe("Testing Search filter", () => {
    const mockStore = configureStore(),
        initialStoreState = {
            filtersParams: {}
        };
    let store;

    beforeEach(() => {
        store = mockStore(initialStoreState);
    });

    it("Update search field value when user enters a value", () => {
        const wrapper = shallow(<SearchFilter store={store}/>).dive(),
            searchInput = wrapper.find("input.bp3-input").first();

        expect(searchInput).toBeDefined();

        const mockedEvent = {
            preventDefault () {},
            target: { value: "foo" }
        };
        expect(wrapper.state("keyword")).toEqual("");
        searchInput.simulate("change", mockedEvent);
        wrapper.update();
        expect(wrapper.state("keyword")).toEqual("foo");
    });
});

问题是模拟更改后状态不会更新,并且测试总是失败。

我该如何解决这个问题,或者是否有其他方法来测试状态更新?

4

1 回答 1

0

评论很便宜,给你看代码:

index.tsx,要测试的 React 组件:

import React, { PureComponent } from 'react';
import { connect, DispatchProp } from 'react-redux';
import { SetFiltersValue } from './actionCreators';

interface ISearchFilterStateProps {
  searchKeyword: string;
}

interface ISearchFilterState {
  keyword: string;
}

type Props = ISearchFilterStateProps & DispatchProp;

class SearchFilter extends PureComponent<Props, ISearchFilterState> {
  constructor(props: Props) {
    super(props);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.state = {
      keyword: props.searchKeyword
    };
  }

  public handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {
    this.setState({ keyword: e.target.value });
    this.props.dispatch(SetFiltersValue());
  }

  public render() {
    const { keyword } = this.state;
    return (
      <div className="search-w bp3-input-group">
        <span className="bp3-icon bp3-icon-search" />
        <input
          className="bp3-input"
          value={keyword}
          type="text"
          placeholder="Search input"
          dir="auto"
          onChange={this.handleInputChange}
        />
      </div>
    );
  }
}

const mapStateToProps = (_, ownParams) => {
  return { searchKeyword: '' };
};

export default connect(mapStateToProps)(SearchFilter);

actionCreators.ts

export const SetFiltersValue = () => ({ type: 'SET_FILTER' });

单元测试:

import React from 'react';
import { shallow } from 'enzyme';
import configureStore from 'redux-mock-store';
import SearchFilter from './';
import { SetFiltersValue } from './actionCreators';

const initialState = { filtersParams: {} };
type State = typeof initialState;
const mockStore = configureStore<State>();

describe('SearchFilter', () => {
  let store;
  beforeEach(() => {
    store = mockStore(initialState);
  });
  it('t1', () => {
    const searchFilter = shallow(<SearchFilter store={store}></SearchFilter>).dive();
    const searchInputWrapper = searchFilter.dive();
    const searchInput = searchInputWrapper.find('input.bp3-input').first();
    expect(searchInputWrapper.state('keyword')).toBe('');

    const mockedEvent = {
      preventDefault: jest.fn(),
      target: { value: 'foo' }
    };
    searchInput.simulate('change', mockedEvent);
    expect(searchInputWrapper.state('keyword')).toBe('foo');
    expect(store.getActions()).toEqual([SetFiltersValue()]);
    expect(searchInputWrapper.html()).toMatchSnapshot();
  });
});


覆盖率 100% 的单元测试结果:

 PASS  src/stackoverflow/54587960/index.spec.tsx
  SearchFilter
    ✓ t1 (28ms)

 › 1 snapshot written.
-------------------|----------|----------|----------|----------|-------------------|
File               |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-------------------|----------|----------|----------|----------|-------------------|
All files          |      100 |      100 |      100 |      100 |                   |
 actionCreators.ts |      100 |      100 |      100 |      100 |                   |
 index.tsx         |      100 |      100 |      100 |      100 |                   |
-------------------|----------|----------|----------|----------|-------------------|
Snapshot Summary
 › 1 snapshot written from 1 test suite.

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   1 written, 1 total
Time:        4.367s, estimated 5s

反应组件快照:

// Jest Snapshot v1

exports[`SearchFilter t1 1`] = `"<div class=\\"search-w bp3-input-group\\"><span class=\\"bp3-icon bp3-icon-search\\"></span><input type=\\"text\\" class=\\"bp3-input\\" value=\\"foo\\" placeholder=\\"Search input\\" dir=\\"auto\\"/></div>"`;

这是完成的演示:https ://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/54587960

于 2019-09-16T07:16:04.443 回答