Friday, September 18, 2020

mocking fetch

Had a rough week with unit test. The status quo of unit testing in React seems a little rough; the fake-y environment you set up to fake being a browser seems at least at delicate as the actual code you want to test...

I had almost forgotten discovering react-testing-library which seems to be the future, and a little closer to unit tests that simulate users rather than Enzyme's way of getting lost in a see of props. There was some learning curve though; the finders like screen.getByLabelText() and screen.getByRole() seem designed to help get devs thinking in terms of a11y issues (like making sure aria roles are accounted for etc) so if you're used to thinking "oh I'll just grab it via the element id".... it kind of discourages that sort of thinking.

I had a surprising amount of aggravation faking the client/server response. I was hoping code like this would set up Mock Service Worker as a nice little endpoint:

import { rest } from 'msw'
import { setupServer } from 'msw/node'

//... later in the test: 

const server = setupServer(
  rest.get('/endpoint*',  (req, res, ctx) => {
    return res(ctx.json({ results: ['abc','def','ghi'] }))
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

Though I'd also have use a polyfill for fetch

const {Response, Request, Headers, fetch} = require('whatwg-fetch');
global.Response = Response;
global.Request = Request;
global.Headers = Headers;
global.fetch = fetch;

But I couldn't get it to work; somehow the fetch().then would never resolve. It's still easy to get trapped up on this asynch stuff!

So after banging my head against it I went with the less recommended practice of handrolling a stubbed out fetch:

const mockSuccessResponse = {results: ['abc','def','ghi']};
const mockJsonPromise = Promise.resolve(mockSuccessResponse);
const mockFetchPromise = Promise.resolve({
  json: () => mockJsonPromise,
global.fetch = jest.fn().mockImplementation(() => {
  return mockFetchPromise;

//..and later
delete global.fetch;

Blargh! Good enough for the moment I guess.

No comments:

Post a Comment