【问题标题】:Mocking & monitoring Keyboard events with jest in react native在本机反应中用玩笑模拟和监视键盘事件
【发布时间】:2019-03-16 22:38:39
【问题描述】:

给定以下代码:

import { Keyboard } from 'react-native';

// ....

componentDidMount() {
  this.keyboardShowListener = Keyboard.addListener(
    'keyboardWillShow',
    () => this.setState({ visible: true }),
  );
  this.keyboardHideListener = Keyboard.addListener(
    'keyboardWillHide',
    () => this.setState({ visible: false }),
  );
}

// ....

onCancel() {
  const { clearActiveInput } = this.props;
  clearActiveInput();
  Keyboard.dismiss();
}

是否有正确的方法来模拟导入的 Keyboard 组件以验证侦听器订阅是否发生,以及验证 dismiss() 事件是否已触发?

【问题讨论】:

  • 你找到解决办法了吗?

标签: unit-testing react-native mocking jestjs


【解决方案1】:

所以这个问题比我想象的要复杂得多。 既然你想在这里测试的是dismiss() 和show() 基本上是一个键盘,对吧?

因此,0.63 和 0.62 文档中的事件和侦听器是 https://reactnative.dev/docs/keyboard#docsNav

useEffect(() => {
   Keyboard.addListener("keyboardDidShow", _keyboardDidShow);
   Keyboard.addListener("keyboardDidHide", _keyboardDidHide);

   // cleanup function
   return () => {
       Keyboard.removeListener("keyboardDidShow", _keyboardDidShow);
       Keyboard.removeListener("keyboardDidHide", _keyboardDidHide);
   };
}, []);

const _keyboardDidShow = () => {
    alert("Keyboard Shown");
};

const _keyboardDidHide = () => {
    alert("Keyboard Hidden");
};

要让 Jest 调用 _keyboardDidShow_keyboardDidHide 这两个函数,您需要使用 Keyboard.emit('keyboardDidShow')

例子:

it('Test Keyboards keyboardDidShow is called', () => {
    const { getByTestId } = render(<Container />);
    act(() => {
        Keyboard.emit('keyboardDidShow', {});
    });
    const box = getByTestId('TEST');
    //Do here your expect clauses to check if something changed in your container
});

不完全确定这是否会对任何人有所帮助。但这就是我解决这个难题的方法,以弄清楚如何覆盖 _keyboardDidShow 和 Hide

编辑: 对于 0.66 版 > 现在您需要执行以下操作:

useEffect(() => {
  const kds = Keyboard.addListener("keyboardDidShow", _keyboardDidShow);
  const kdh = Keyboard.addListener("keyboardDidHide", _keyboardDidHide);

  // cleanup function
  return () => {
    kdh.remove();
    kds.remove();
  };
}, []);

const _keyboardDidShow = () => {
  alert("Keyboard Shown");
};

const _keyboardDidHide = () => {
  alert("Keyboard Hidden");
};

因为他们从现在开始改变了订阅事件的方式

对于测试,将 Keyboard.emit 更改为:

Keyboard._emitter.emit('keyboardDidShow', {});
Keyboard._emitter.emit('keyboardDidHide', {});

你应该拥有让它开玩笑工作所需的一切:)

【讨论】:

【解决方案2】:

从 React Native 65 开始,jest 失败了: TypeError: _reactNative.Keyboard.emit is not a function

我还没有找到解决方案...我认为键盘上没有任何变化,根据反应本机文档。仅供参考

编辑:用 DeviceEventEmitter.emit('keyboardWillShow')修复

【讨论】:

  • 尝试使用:Keyboard._emitter.emit('keyboardDidShow', {});
【解决方案3】:

编辑:请使用@Thiago de Oliveira Cruz 提供的方法,该方法更干净,更易于使用。

我使用this article 中提到的方法来“触发”'keyboardDidShow''keyboardDidHide' 事件。基本上,我们模拟实现keyboard.addListener 并使用映射来记录事件到回调的映射。这样,在安装组件后,我们就可以访问与'keyboardDidShow''keyboardDidHide' 事件关联的回调。然后我们直接调用回调,就好像事件被触发了一样。

这显然是一种解决方法,但它适用于我的用例。下面是一个简单的示例,说明如何测试使用'keyboardDidShow''keyboardDidHide' 事件来控制是否显示一段文本的组件。有关该示例的详细说明,请参阅here

示例组件:

import * as React from 'react';
import {View, Text, Keyboard} from 'react-native';

const MyComponent = () => {
  const [showText, setShowText] = React.useState(true);

  // Use keyboard event (https://reactnative.dev/docs/keyboard)
  React.useEffect(() => {
    Keyboard.addListener('keyboardDidShow', _keyboardDidShow);
    Keyboard.addListener('keyboardDidHide', _keyboardDidHide);

    // cleanup function
    return () => {
      Keyboard.removeListener('keyboardDidShow', _keyboardDidShow);
      Keyboard.removeListener('keyboardDidHide', _keyboardDidHide);
    };
  }, []);

  const _keyboardDidShow = () => setShowText(false);
  const _keyboardDidHide = () => setShowText(true);

  if (showText) {
    return (
      <View>
        <Text>Bear, Beets, Battlestar Galactica</Text>
      </View>
    );
  }
  return null;
};

export {MyComponent};

测试代码

import * as React from 'react';
import {Keyboard} from 'react-native';
import {mount} from 'enzyme';
import {MyComponent} from 'mycomponent.js';

describe('Test MyComponent', () => {
  const mockSetShowText = jest.fn();
  const mockUseState = jest.spyOn(React, 'useState');
  const mockKeyboardListener = jest.spyOn(Keyboard, 'addListener');
  const keyboardCallbackMap = {};

  afterAll(() => {
    mockSetShowText.mockReset();
    mockKeyboardListener.mockRestore();
    mockUseState.mockRestore();
  });

  test('Text shown by default', () => {
    const myComponent = mount(<MyComponent />);
    expect(myComponent.find('Text').first()).toHaveText(
      'Bear, Beets, Battlestar Galactica',
    );
  });

  test('Text not show when showText is false', () => {
    mockUseState.mockReturnValue([false, mockSetShowText]);
    const myComponent = mount(<MyComponent />);
    expect(myComponent.find('Text')).not.toExist();
  });

  test('When Keyboard shows up, set showText to false; when disappear, set showText to true', () => {
    mockKeyboardListener.mockImplementation((event, cb) => {
      keyboardCallbackMap[event] = cb;
    });
    mockUseState.mockImplementation(showText => [showText, mockSetShowText]);
    const myComponent = mount(<MyComponent />);
    keyboardCallbackMap.keyboardDidShow(); // mock keyboardDidShow event
    expect(mockSetShowText).toHaveBeenCalledWith(false);
    keyboardCallbackMap.keyboardDidHide(); // mock keyboardDidHide event
    expect(mockSetShowText).toHaveBeenCalledWith(true);
    myComponent.unmount();
  });
});

【讨论】:

    【解决方案4】:

    订阅Keyboard 事件的组件也遇到了类似的问题

    const MyComponent = () => {
        useEffect(() => {
            const listener = Keyboard.addListener('keyboardDidHide', () => {})
    
            return () => {
                listener.remove()
            }
        })
    
        return <View>...</View>
    }
    

    我能够通过以下测试测试Keyboard.addListener,并且还测试了在组件卸载时正在调用listener.remove

    import renderer from 'react-test-renderer'
    
    const mockListener = {
        remove: jest.fn(),
    }
    const originalAddListener = Keyboard.addListener
    const mockAddListener = jest.fn().mockReturnValue(mockListener)
    
    describe('<MyComponent />', () => {
        beforeAll(() => {
            Keyboard.addListener = mockAddListener
        })
        beforeEach(() => {
            mockAddListener.mockClear()
            mockListener.remove.mockClear()
        })
        afterAll(() => {
            Keyboard.addListener = originalAddListener
        })
        it('should subscribe to KeyboardDidClose event', () => {
            renderer.create(<MyComponent />)
            expect(Keyboard.addListener).toHaveBeenCalled()
        })
    
        it('should call listener.remove on unmount', () => {
            const component = renderer.create(
                < MyComponent />,
            )
            component.unmount()
            expect(mockListener.remove).toHaveBeenCalled()
        })
    })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-22
      • 2021-01-17
      • 2017-07-27
      • 2019-06-14
      • 1970-01-01
      • 2023-04-04
      • 2020-10-05
      • 2022-06-13
      相关资源
      最近更新 更多