首页 > 解决方案 > 酶按钮模拟无法按预期工作

问题描述

我有以下反应原生测试代码。

import { shallow } from 'enzyme';
import React from 'react';

import {
  BorderlessButton,
  InputBox,
  ProgressBar,
} from 'components';

import Name from '../name.component';

describe('Name component', () => {
  let wrapper: any;

  const mockOnPress = jest.fn();
  const mockSaveStep = jest.fn();

  const mockProps: any = {
    errors: null,
    values: [{ givenName: 'givenName', familyName: 'familyName' }],
  };

  beforeEach(() => {
    wrapper = shallow(<Name signUpForm={mockProps} saveStep={mockSaveStep} />);
  });

  it('should render Name component', () => {
    expect(wrapper).toMatchSnapshot();
  });

  it('should render 2 <InputBox />', () => {
    expect(wrapper.find(InputBox)).toHaveLength(2);
  });

  it('should render a <ProgressBar />', () => {
    expect(wrapper.find(ProgressBar)).toHaveLength(1);
  });

  it('should render a <BorderlessButton /> with the text NEXT', () => {
    expect(wrapper.find(BorderlessButton)).toHaveLength(1);
    expect(wrapper.find(BorderlessButton).props().text).toEqual('NEXT');
  });

  it('should press the NEXT button', () => {
    wrapper.find(BorderlessButton).simulate('click');
    expect(mockOnPress).toHaveBeenCalled();
  });
});

但是最后一个测试不能正常工作。如何模拟此按钮单击?这给了我一个错误说

期望(jest.fn()).toHaveBeenCalled()。预期的模拟函数已被调用,但未被调用。

这是组件。

class NameComponent extends Component {
  componentDidMount() {
    const { saveStep } = this.props;
    saveStep(1, 'Name');
  }

  disableButton = () => {
    const {
      signUpForm: {
        errors, values,
      },
    } = this.props;

    if (errors && values && errors.givenName && errors.familyName) {
      if (errors.givenName.length > 0 || values.givenName === '') return true;
      if (errors.familyName.length > 0 || values.familyName === '') return true;
    }
  }

  handleNext = () => {
    navigationService.navigate('PreferredName');
  }

  resetForm = () => {
    const { resetForm } = this.props;
    resetForm(SIGN_UP_FORM);
    navigationService.navigate('LoginMain');
  }

  render() {
    const { name, required } = ValidationTypes;
    const { step } = this.props;

    return (
      <SafeAreaView style={{ flex: 1 }}>
        <KeyboardAvoidingView style={{ flex: 1 }}
          behavior={Platform.OS === 'ios' ? 'padding' : null}
          enabled>
          <ScreenContainer
            navType={ScreenContainer.Types.LEVEL_THREE}
            levelThreeOnPress={this.resetForm}>

            <View style={styles.container}>
              <View style={{ flex: 1 }}>
                <SinglifeText
                  type={SinglifeText.Types.H1}
                  label='Let’s start with your legal name'
                  style={styles.textLabel}
                />

                <View style={styles.names}>
                  <InputBox
                    name='givenName'
                    form={SIGN_UP_FORM}
                    maxLength={22}
                    placeholder='Given name'
                    containerStyle={styles.givenNameContainer}
                    inputContainerStyle={styles.inputContainer}
                    errorStyles={styles.inputError}
                    keyboardType={KeyBoardTypes.default}
                    validations={[required, name]}
                  />
                  <InputBox
                    name='familyName'
                    form={SIGN_UP_FORM}
                    maxLength={22}
                    placeholder='Family name'
                    inputContainerStyle={styles.inputContainer}
                    errorStyles={styles.inputError}
                    keyboardType={KeyBoardTypes.default}
                    validations={[required, name]}
                  />
                </View>

                <SinglifeText
                  type={SinglifeText.Types.HINT}
                  label='Please use the same name you use with your bank'
                  style={styles.hint}
                />
              </View>
            </View>
          </ScreenContainer>

          <ProgressBar presentage={(step / MANUAL_SIGNUP_STEP_COUNT) * 100} />

          <View style={styles.bottomButtonContainer}>
            <BorderlessButton
              text='NEXT'
              disabled={this.disableButton()}
              onPress={this.handleNext}
            />
          </View>

        </KeyboardAvoidingView>
      </SafeAreaView>
    );
  }
}

我该如何解决这个问题?

标签: javascriptreactjsreact-nativejestjsenzyme

解决方案


您创建函数mockOnPress(),但mockOnPress()从未注入到组件中。

在您编写的组件中,NameComponent有一个子BorderlessButton组件,其中的行onPress={this.handleNext}是硬编码的。handleNext()在其他地方定义为:

handleNext = () => {
    navigationService.navigate('PreferredName');
}

为了测试按钮的功能是否正常工作,我看到了两个可行的选项。一种是使用依赖注入navigationService.navigate('PreferredName')您可以让它执行作为道具传入的代码,而不是硬编码要调用的按钮。请参阅以下示例:

it('Button should handle simulated click', function (done) {
  wrappedButton = mount(<MyButton onClick={() => done()}>Click me!</BaseButton>)
  wrappedButton.find('button').simulate('click')
}

请注意,您可以采用上面示例中提供的原理并将其扩展为您的示例,方法是将您希望在 onClick 上发生的功能作为道具传递给您的 NameComponent。

您可以选择的另一个选项是测试单击按钮是否会导致您想要发生的副作用。正如所写,按下按钮应该调用,navigationService.navigate('PreferredName')。这是预期的效果吗?如果是这样,您可以更改测试以验证是否navigationService.navigate('PreferredName')以某种方式调用。


推荐阅读