Skip to content

Commit b22fd8a

Browse files
committed
last example
1 parent 3597b0c commit b22fd8a

File tree

1 file changed

+72
-44
lines changed

1 file changed

+72
-44
lines changed

versioned_docs/version-7.x/testing.md

Lines changed: 72 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ We get tab bar buttons, press buttons and check if rendered screens are correct.
650650

651651
### Example 4
652652

653-
Display loading state while waiting for data and then fetched profile nick on every profile screen focus.
653+
On every profile screen focus, display loading state while waiting for data and then show fetched profile.
654654

655655
<Tabs groupId="example" queryString="example">
656656
<TabItem value="static" label="Static" default>
@@ -678,16 +678,29 @@ function ProfileScreen() {
678678

679679
useFocusEffect(
680680
useCallback(() => {
681-
fetch(url)
682-
.then((res) => res.json())
683-
.then((data) => setData(data))
684-
.catch((error) => setError(error))
685-
.finally(() => setLoading(false));
681+
let isActive = true;
682+
683+
const fetchUser = async () => {
684+
try {
685+
const data = await (await fetch(url)).json();
686+
687+
if (isActive) {
688+
setData(data);
689+
setLoading(false);
690+
}
691+
} catch (error) {
692+
setError(error);
693+
setLoading(false);
694+
}
695+
};
696+
697+
fetchUser();
686698

687699
return () => {
688700
setData(undefined);
689701
setError(undefined);
690702
setLoading(true);
703+
isActive = false;
691704
};
692705
}, [])
693706
);
@@ -703,8 +716,18 @@ function ProfileScreen() {
703716

704717
export const TabNavigator = createBottomTabNavigator({
705718
screens: {
706-
Home: HomeScreen,
707-
Profile: ProfileScreen,
719+
Home: {
720+
screen: HomeScreen,
721+
options: {
722+
tabBarButtonTestID: 'homeTabBarButton',
723+
},
724+
},
725+
Profile: {
726+
screen: ProfileScreen,
727+
options: {
728+
tabBarButtonTestID: 'profileTabBarButton',
729+
},
730+
},
708731
},
709732
screenOptions: {
710733
headerShown: false,
@@ -738,16 +761,29 @@ function ProfileScreen() {
738761

739762
useFocusEffect(
740763
useCallback(() => {
741-
fetch(url)
742-
.then((res) => res.json())
743-
.then((data) => setData(data))
744-
.catch((error) => setError(error))
745-
.finally(() => setLoading(false));
764+
let isActive = true;
765+
766+
const fetchUser = async () => {
767+
try {
768+
const data = await (await fetch(url)).json();
769+
770+
if (isActive) {
771+
setData(data);
772+
setLoading(false);
773+
}
774+
} catch (error) {
775+
setError(error);
776+
setLoading(false);
777+
}
778+
};
779+
780+
fetchUser();
746781

747782
return () => {
748783
setData(undefined);
749784
setError(undefined);
750785
setLoading(true);
786+
isActive = false;
751787
};
752788
}, [])
753789
);
@@ -766,8 +802,16 @@ const Tab = createBottomTabNavigator();
766802
export function TabNavigator() {
767803
return (
768804
<Tab.Navigator screenOptions={{ headerShown: false }}>
769-
<Tab.Screen name="Home" component={HomeScreen} />
770-
<Tab.Screen name="Profile" component={ProfileScreen} />
805+
<Tab.Screen
806+
name="Home"
807+
component={HomeScreen}
808+
options={{ tabBarButtonTestID: 'homeTabBarButton' }}
809+
/>
810+
<Tab.Screen
811+
name="Profile"
812+
component={ProfileScreen}
813+
options={{ tabBarButtonTestID: 'profileTabBarButton' }}
814+
/>
771815
</Tab.Navigator>
772816
);
773817
}
@@ -782,7 +826,7 @@ export function TabNavigator() {
782826
```js
783827
import { expect, jest, test } from '@jest/globals';
784828
import { createStaticNavigation } from '@react-navigation/native';
785-
import { act, fireEvent, render, screen } from '@testing-library/react-native';
829+
import { fireEvent, render, screen } from '@testing-library/react-native';
786830

787831
import { TabNavigator } from './TabNavigator';
788832

@@ -801,33 +845,24 @@ async function mockedFetch() {
801845
};
802846
}
803847

804-
test('Display loading state while waiting for data and then fetched profile nick on every profile screen focus', async () => {
805-
jest.useFakeTimers();
806-
848+
test('on every profile screen focus, displays loading state while waiting for data and then shows fetched profile', async () => {
807849
const TabNavigation = createStaticNavigation(TabNavigator);
808850
render(<TabNavigation />);
809851

810852
const spy = jest.spyOn(window, 'fetch').mockImplementation(mockedFetch);
811853

812-
const homeTabButton = screen.getByRole('button', {
813-
name: 'Home, tab, 1 of 2',
814-
});
815-
816-
const profileTabButton = screen.getByRole('button', {
817-
name: 'Profile, tab, 2 of 2',
818-
});
854+
const homeTabButton = screen.getByTestId('homeTabBarButton');
855+
const profileTabButton = screen.getByTestId('profileTabBarButton');
819856

820857
const event = {};
821858
fireEvent.press(profileTabButton, event);
822-
act(() => jest.runAllTimers());
823859

824860
expect(screen.queryByText('Loading')).toBeOnTheScreen();
825861
expect(spy).toHaveBeenCalled();
826862
expect(await screen.findByText('CookieDough')).toBeOnTheScreen();
827863

828864
fireEvent.press(homeTabButton, event);
829865
fireEvent.press(profileTabButton, event);
830-
act(() => jest.runAllTimers());
831866

832867
expect(screen.queryByText('Loading')).toBeOnTheScreen();
833868
expect(spy).toHaveBeenCalled();
@@ -841,7 +876,7 @@ test('Display loading state while waiting for data and then fetched profile nick
841876
```js
842877
import { expect, jest, test } from '@jest/globals';
843878
import { NavigationContainer } from '@react-navigation/native';
844-
import { act, fireEvent, render, screen } from '@testing-library/react-native';
879+
import { fireEvent, render, screen } from '@testing-library/react-native';
845880

846881
import { TabNavigator } from './TabNavigator';
847882

@@ -860,9 +895,7 @@ async function mockedFetch() {
860895
};
861896
}
862897

863-
test('Display loading state while waiting for data and then fetched profile nick on every profile screen focus', async () => {
864-
jest.useFakeTimers();
865-
898+
test('on every profile screen focus, displays loading state while waiting for data and then shows fetched profile', async () => {
866899
render(
867900
<NavigationContainer>
868901
<TabNavigator />
@@ -871,26 +904,20 @@ test('Display loading state while waiting for data and then fetched profile nick
871904

872905
const spy = jest.spyOn(window, 'fetch').mockImplementation(mockedFetch);
873906

874-
const homeTabButton = screen.getByRole('button', {
875-
name: 'Home, tab, 1 of 2',
876-
});
877-
const profileTabButton = screen.getByRole('button', {
878-
name: 'Profile, tab, 2 of 2',
879-
});
907+
const homeTabButton = screen.getByTestId('homeTabBarButton');
908+
const profileTabButton = screen.getByTestId('profileTabBarButton');
880909

881910
const event = {};
882911
fireEvent.press(profileTabButton, event);
883-
act(() => jest.runAllTimers());
884912

885-
expect(screen.queryByText('Loading')).toBeOnTheScreen();
913+
expect(screen.getByText('Loading')).toBeOnTheScreen();
886914
expect(spy).toHaveBeenCalled();
887915
expect(await screen.findByText('CookieDough')).toBeOnTheScreen();
888916

889917
fireEvent.press(homeTabButton, event);
890918
fireEvent.press(profileTabButton, event);
891-
act(() => jest.runAllTimers());
892919

893-
expect(screen.queryByText('Loading')).toBeOnTheScreen();
920+
expect(screen.getByText('Loading')).toBeOnTheScreen();
894921
expect(spy).toHaveBeenCalled();
895922
expect(await screen.findByText('CookieDough')).toBeOnTheScreen();
896923
});
@@ -899,7 +926,7 @@ test('Display loading state while waiting for data and then fetched profile nick
899926
</TabItem>
900927
</Tabs>
901928

902-
We query tab buttons and mock fetch function using `spyOn` and `mockImplementation`. We navigate to profile screen and check if loading state is rendered correctly. Then, to check if fetched data is displayed, we use `findByText` - we need to wait for the fetch to finish before checking it's result. To ensure that operation will succeed not only on the first focus, we navigate back to home, then to settings and check loading state and fetched data again.
929+
We query tab buttons and mock fetch function using `spyOn` and `mockImplementation`. We navigate to profile screen and check if loading state is rendered correctly. Then, to check if fetched data is displayed, we use `findByText` - we need to wait for the fetch to finish before checking its result. To ensure that operation will succeed not only on the first focus, we navigate back to home, then to settings and check loading state and fetched data again.
903930

904931
To make test deterministic and isolate it from the real backend you can mock fetch function. You can use `spyOn` to override real implementation of fetch with `mockedFetch`.
905932

@@ -920,7 +947,7 @@ async function mockedFetch() {
920947
};
921948
}
922949

923-
test('display loading state while waiting for data and then fetched profile nick on every profile screen focus', async () => {
950+
test('on every profile screen focus, displays loading state while waiting for data and then shows fetched profile', async () => {
924951
// ...
925952

926953
// Replace fetch implementation with mock
@@ -941,3 +968,4 @@ There are a couple of things to keep in mind when writing tests for components u
941968

942969
1. Avoid mocking React Navigation. Instead, use a real navigator in your tests.
943970
2. Don't check for navigation actions. Instead, check for the result of the navigation such as the screen being rendered.
971+
3. Remember to use fake timers when testing code that involves animations (e.g. transitions between screens with `StackNavigator`).

0 commit comments

Comments
 (0)