How would you mock 'onPress' within Alert?
Asked Answered
I

3

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?

Improper answered 7/9, 2017 at 2:55 Comment(0)
D
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)
})
Dituri answered 7/9, 2017 at 9:34 Comment(11)
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 )Improper
Updated the answer to reflect your acual questionDeepen
Worked for me! Thanks!Scarlettscarp
it doesn't work for me expect(jest.fn())[.not].toHaveBeenCalled() jest.fn() value must be a mock function or spy. Atom
@Atom try: Alert.alert = jest.fn();Bricklaying
@AndreasKöberle can you explain what is the meaning of mock.calls[0][2][0]?Orientalism
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])Deepen
@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"Orientalism
@Orientalism please create a new question for thisDeepen
@AndreasKöberle #54435594Orientalism
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.Gauffer
W
11

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();
Wasting answered 9/2, 2021 at 12:42 Comment(1)
Thanks. Saved me a lot of work figuring this out!Hight
N
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),
)
Numb answered 15/3, 2023 at 7:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.