How to test Alert in React Native with Jest
Asked Answered
D

3

9

So in my program, when a user logs in, if all credentials are correct they will proceed to the next page if any details are missing or incorrect format, and alert is shown on screen.

How do I test using Jest in React Native if the alert has been displayed after a button press, and confirm that the text of the alert is correct?

Some of my component is shown below:

...
.catch(function(error) {
    // Handle Errors here.
    var errorCode = error.code;
    var errorMessage = error.message;
    alert(errorMessage)
  });

The error text is generated by Google's Firebase, but I do know what it is.

Designed answered 9/4, 2020 at 13:42 Comment(0)
A
26

Assuming you are using react-native's Alert and calling it yourself, you can spy on it like so:

import { Alert } from 'react-native';

jest.spyOn(Alert, 'alert');

You can then check that it has been called, and what arguments were used:

expect(Alert.alert).toHaveBeenCalledWith(errorMessageText)

[Update] it is also possible to test interactions with the alert:

const mockOnPress = jest.fn()

Alert.alert('Title', 'Message', [{text: 'OK', onPress: mockOnPress}])

/** 
 * here we inspect the mock to get:
 * - the latest call to Alert.alert (`calls[0]`)
 * - its third argument (`calls[0][2]`), which is the buttons config array
 * - the first button in the array and its onPress (`calls[0][2][0].onPress()`)
 */
Alert.alert.mock.calls[0][2][0].onPress()

expect(mockOnPress).toBeCalled()
Amanuensis answered 9/4, 2020 at 16:1 Comment(7)
I'm really new in testing and took long for me to realize that I also need to call the Alert.alert("") (or a function that calls Alert.alert) to pass the the test. Thanks for helping meHindbrain
Is there a way to test user interaction with the alert? Ie. fireEvent.press on the "ok" or "cancel"?Vigesimal
@Vigesimal edited to add an example of how to test interactions. the alert isn't actually 'rendered' under test, so the only way (that I know of) is to inspect the mock and manually trigger the onPress that was passed to it.Amanuensis
Excellent answer! I'd like to note that Alert.alert.mock.calls[0] is only the latest call if there was only one call in total. I had to adjust that to pick the last element of the array in a test.Lioness
I get this error: Property 'mock' does not exist on type '(title: string, message?: string | undefined, buttons?: AlertButton[] | undefined, options?: AlertOptions | undefined) => void'.Pugging
@IshitaSinha if you are using typescript, then you might need to create a spy first with const alertSpy = jest.spyOn(Alert, 'alert') and then use alertSpy.mock to access the callbackAmanuensis
Yes, I did that. Even then I kept getting that error.Pugging
J
1

in your spec file.

add

export class FakeSubject {
    next(value: any) {}
    asObservable() {}
}

configure your testbed:

TestBed.configureTestingModule({
    providers: [{ provide: Subject, useClass: FakeSubject }],
}),

add a service getter before each test.

beforeEach(() => {
    service = TestBed.get(AlertService);
});

add test, you can use this example for another test.

it('success alert ', () => {
    const spy = spyOn(service, 'alert');
    const message = 'hi!';
    service.success(message);
    expect(spy).toHaveBeenCalledWith(new Alert(message, AlertType.Success));
});

and your utility methods:

it('alert ', () => {
    subject = service['subject'];
    const spy = spyOn(subject, 'next');
    const alert = new Alert('hi', AlertType.Success);
    service.alert(alert);
    expect(spy).toHaveBeenCalledWith(alert);
});

it('clear ', () => {
    subject = service['subject'];
    const spy = spyOn(subject, 'next');

service.clear();
expect(spy).toHaveBeenCalledWith(null);
});
Jealousy answered 30/3, 2021 at 3:40 Comment(0)
O
0

StackOverflow won't let me comment on the existing approved answer, but to add for others using TypeScript trying to follow the accepted answer:

[Update] it is also possible to test interactions with the alert:

const mockOnPress = jest.fn()

Alert.alert('Title', 'Message', [{text: 'OK', onPress: mockOnPress}])

/** 
 * here we inspect the mock to get:
 * - the latest call to Alert.alert (`calls[0]`)
 * - its third argument (`calls[0][2]`), which is the buttons config array
 * - the first button in the array and its onPress (`calls[0][2][0].onPress()`)
 */
Alert.alert.mock.calls[0][2][0].onPress()

expect(mockOnPress).toBeCalled()

To avoid the [0][2][0] portion giving TS errors (and not just resorting to @ts-ignore) around potentially being undefined, I created a custom type setting the props to match how we're using the Alert.alert in the code being tested so the buttons will be defined:

type AlertSpyProps = jest.SpyInstance<
  void,
  [
    title: string,
    message: string,
    buttons: [
      {
        text: string
        style: AlertButton['style']
        onPress: (value?: string) => void
      },
      {
        text: string
        style: AlertButton['style']
        onPress: (value?: string) => void
      },
    ],
  ]
>
const AlertSpy = jest.spyOn(Alert, 'alert') as AlertSpyProps

After that TS no longer complains about the [0][2][0] potentially being undefined.

Oira answered 9/5 at 18:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.