21

I'm able to mock out the Alert to test that it's alert method is being called, but what I really want to test is pressing the ok button within the alert.

import { Alert } from 'react-native';

it('Mocking Alert', () => {
    jest.mock('Alert', () => {
        return {
          alert: jest.fn()
          }
        };
      });

    const spy = jest.spyOn(Alert, 'alert');
    const wrapper = shallow(<Search />);

    wrapper.findWhere(n => n.props().title == 'Submit').simulate('Press');
    expect(spy).toHaveBeenCalled(); //passes
})

I'm absolutely unsure of how to test for that though. Here is a generic component with what I am trying to test.

export default class Search extends Component{

    state = {
      someState: false
    }

    confirmSubmit(){
      this.setState(state => ({someState: !state.someState}))
    }

    onPress = () => {
      Alert.alert(
        'Confirm',
        'Are you sure?'
        [{text: 'Ok', onPress: this.confirmSubmit}] //<-- want to test this
      )
    }

    render(){
      return(
       <View>
         <Button title='Submit' onPress={this.onPress}
       </View>
      )
    }
}

Has anyone ever attempted this?

Anthony Urbina
  • 381
  • 5
  • 12

3 Answers3

24

I would mock the module and import it to test on the spy. Then trigger the click event. This will call spy. From the spy you can get the params it was called with using mock.calls to get the onPress method and call it. Then you can test the state of your component.

import Alert from 'Alert'

jest.mock('Alert', () => {
    return {
      alert: jest.fn()
    }
});


it('Mocking Alert', () => {
    const wrapper = shallow(<Search />);
    wrapper.findWhere(n => n.props().title == 'Submit').simulate('Press');
    expect(Alert.alert).toHaveBeenCalled(); // passes
    Alert.alert.mock.calls[0][2][0].onPress() // trigger the function within the array
    expect(wrapper.state('someState')).toBe(true)
})
carla
  • 1,970
  • 1
  • 31
  • 44
Andreas Köberle
  • 106,652
  • 57
  • 273
  • 297
  • 1
    Sorry, I should have included that in my question. That's essentially what I'm doing already. I have Alert imported in the test. What I want to test is the onPress within the alert method `Alert.alert( 'Confirm', 'Are you sure?' [{text: 'Ok', onPress: this.confirmSubmit}] //<-- want to test this )` – Anthony Urbina Sep 07 '17 at 13:32
  • 1
    Updated the answer to reflect your acual question – Andreas Köberle Sep 07 '17 at 13:41
  • Worked for me! Thanks! – Moyote Jun 13 '18 at 09:23
  • 2
    it doesn't work for me `expect(jest.fn())[.not].toHaveBeenCalled() jest.fn() value must be a mock function or spy. ` – marco Aug 09 '18 at 15:22
  • @marco try: ```Alert.alert = jest.fn();``` – MaxKonin Aug 27 '18 at 11:31
  • @AndreasKöberle can you explain what is the meaning of mock.calls[0][2][0]? – Yossi Jan 15 '19 at 14:34
  • 2
    From the first call of the mock (`mock.calls[0]`) get the third argument ([2]), which is the array `[{text: 'Ok', onPress: this.confirmSubmit}]`, so get the first item from it ([0]) – Andreas Köberle Jan 15 '19 at 14:51
  • @AndreasKöberle my apologies for bothering you again on this... I am using, instead of Alert.alert, an async alert from github.com/slorber/react-native-alert-async. Any idea how I can define it in a similar way that you did for Alert.alert? I tried: jest.mock('react-native-alert-async', () => { return { AlertAsync: jest.fn() } }), and then "AlertAsync.calls[0][2][1].onPress()", but I am getting "TypeError: Cannot read property '0' of undefined" – Yossi Jan 29 '19 at 07:42
  • 1
    @Yossi please create a new question for this – Andreas Köberle Jan 29 '19 at 09:34
  • @AndreasKöberle https://stackoverflow.com/questions/54435094/how-would-you-mock-onpress-within-a-third-party-library – Yossi Jan 30 '19 at 07:13
  • 1
    if `Alert.alert.mock.calls[0][2][0].onPress()` gives off compile time error, such as `Property 'mock' does not exist on type '(title: string, message?: string, buttons?: AlertButton[], options?: AlertOptions, type?: string) => void'.`, then use `Alert.alert['mock'].calls[0][2][0].onPress()` notation. – Nar Gar Jul 24 '19 at 22:29
10

I had the same problem with testing Alert and trying to simulate onPress for the Alert. I'm implementing my code with TypeScript. I managed to handle this by using spyOn like:

const spyAlert = jest.spyOn(Alert, 'alert');

and then to use onPress you need to ignore type checking for the line otherwise you'll get - Cannot invoke an object which is possibly 'undefined'.

// @ts-ignore
spyAlert.mock.calls[0][2][0].onPress();
Kiril Dobrev
  • 839
  • 1
  • 8
  • 12
0

Here's a mock implementation of the Alert that automatically calls the last buttons callback:

jest.spyOn(Alert, 'alert').mockImplementation((title, message, buttons) => {
  if (!buttons) return
  const lastButton = buttons[buttons.length - 1]
  lastButton.onPress && lastButton.onPress()
})

You could add it to a jest.setup.ts file to have it for every test or alternatively add it inside the tests where you need it

Either way you can check that it was called: expect(Alert.alert).toHaveBeenCalled()

And that it's been called with the right info:

expect(Alert.alert).toHaveBeenCalledWith(
  'Confirm',
  'Are you sure?',
   expect.any(Array),
)