Reputation: 59
I encountered problems while testing my demo React app.
I use msw to intercept Firebase API requests and I continuously keep getting error in my terminal.
Please note, that I have Firebase Authentication implemented so only authorized users in my app can read
.
https://travelsi-default-rtdb.firebaseio.com/posts.json?auth=USER-ACCESS-TOKEN
However does user access token (attached to url) really matter if I use msw to mock/intercept fetch requests and fake the response? Or what else causes the error when running test?
console.error
GET https://travelsi-default-rtdb.firebaseio.com/posts.json?auth= net::ERR_FAILED
at FetchInterceptor.<anonymous> (node_modules/@mswjs/interceptors/src/interceptors/fetch/index.ts:91:17)
at step (node_modules/@mswjs/interceptors/lib/interceptors/fetch/index.js:59:23)
at Object.next (node_modules/@mswjs/interceptors/lib/interceptors/fetch/index.js:40:53)
at fulfilled (node_modules/@mswjs/interceptors/lib/interceptors/fetch/index.js:31:58)
For testing I use React Testing Library & Jest. I use msw (Mock Service Worker) for intercepting API calls. The app itself uses Firebase Real Time Database & Firebase Auth, Redux Toolkit for state management.
I am testing AllPosts.js
component where I dispatch action creator thunk dispatch(fetchPosts(accessToken)
that sends a GET
request to Firebase API.
AllPosts.js
import classes from './AllPosts.module.css';
import SinglePost from './SinglePost';
import { fetchPosts } from '../../../../store/slices/action-creators/fetchPosts';
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
const AllPosts = () => {
const dispatch = useDispatch();
const posts = useSelector(state => state.posts.postsToRender);
const { accessToken } = useSelector(state => state.auth.userData);
useEffect(() => {
dispatch(fetchPosts(accessToken));
}, []);
return (
<div className={classes['all-posts']}>
<div className={classes['all-posts__wrapper']}>
{posts.map(post => {
return (
<SinglePost
city={post.city}
country={post.country}
date={post.date}
description={post.description}
id={post.id}
img={post.img}
tag={post.tag}
title={post.title}
feelings={post.feelings}
key={post.id}
/>
);
})}
</div>
</div>
);
};
export default AllPosts;
fetchPosts.js
import { postsActions } from '../posts-slice';
import { FIREBASE_API } from '../../../config/firebase';
import { uiActions } from '../ui-slice';
export const fetchPosts = accessToken => {
return async dispatch => {
const fetchData = async () => {
const res = await fetch(`${FIREBASE_API}/posts.json?auth=${accessToken}`);
if (!res.ok) throw new Error('Problems with getting data. Please try again 🤔.');
const data = await res.json();
const posts = [];
for (const key in data) {
posts.push(data[key]);
}
return posts;
};
try {
dispatch(
uiActions.setNotification({
status: 'loading',
type: 'get',
message: '🦢 loading posts ...',
})
);
const posts = await fetchData();
dispatch(postsActions.getPosts({ posts: posts }));
dispatch(
uiActions.setNotification({
status: 'success',
type: 'get',
message: 'completed',
})
);
} catch (err) {
dispatch(
uiActions.setNotification({
status: 'error',
type: 'get',
message: err.message,
})
);
}
};
};
AllPosts.test.js
import { screen, render } from '@testing-library/react';
import AllPosts from './AllPosts';
import { Provider } from 'react-redux';
import { createStore } from '../../../../store/index';
describe('AllPosts component', () => {
test('posts are rendered correctly', async () => {
render(
<Provider store={createStore()}>
<AllPosts />
</Provider>
);
await wait();
screen.debug();
});
});
const wait = () => new Promise(resolve => setTimeout(resolve, 2000)); //DUMMY FUNCTION USED JUST FOR MY PURPOSES
MY MSW SETUP Nothing fancy here. I actually just followed msw docs.
handlers.js
import { rest } from 'msw';
export const handlers = [
rest.get('https://travelsi-default-rtdb.firebaseio.com/posts.json', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
p1: {
city: 'city 1',
country: 'country',
date: '01.01.2020',
description: 'description',
feelings: {
f1: {
reaction: 1,
},
},
id: p2,
img: 'img',
tag: '#tag',
title: 'title 2',
},
p1: {
city: 'city 2',
country: 'country',
date: '01.01.2020',
description: 'description',
feelings: {
f1: {
reaction: 1,
},
},
id: p2,
img: 'img',
tag: '#tag',
title: 'title 2',
},
})
);
}),
];
server.js
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
setupTests.js
import '@testing-library/jest-dom';
import { server } from './mocks/server';
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
What causes the error? How can I successfully intercept Firebase API calls with msw, get fake response and render items in test?
Thank you in advance. If you need any other implementation details, please let me know ;)
Upvotes: 1
Views: 1360
Reputation: 513
Just ran into a similar issue. What the problem was for me, and what I think it is here, is that the mocked URL is https://travelsi-default-rtdb.firebaseio.com/posts.json without any query string parameters. However, your API call has a "?auth=" at the end of it.
But the msw documentation reads:
Provided an exact request URL string, only those request that strictly match that string are mocked.
https://mswjs.io/docs/basics/request-matching
So per the documentation, you'd want to do one of these two options:
// Matches:
// - /users/admin
// - /users/octocat
rest.get('/users/*', responseResolver),
or:
import { setupWorker, rest } from 'msw'
setupWorker(
// Given "POST https://api.backend.dev/user/abc-123" request,
rest.post('https://api.backend.dev/user/:userId', (req, res, ctx) => {
// `userId` value becomes "abc-123"
const { userId } = req.params
}),
)
Hope this helps any future fellow googlers and/or ChatGPT users!
Upvotes: 0