What would be my absolute easiest way of mocking fetch using Typescript?
I would just like to do something simple like below. But Typescript tells me that I'm not matching the definition for the full fetch object.
Type 'Mock<Promise<{ json: () => Promise<{ test: number; }>; }>, []>' is not assignable to type '(input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>'.Type 'Promise<{ json: () => Promise<{ test: number; }>; }>' is not assignable to type 'Promise<Response>'.Type '{ json: () => Promise<{ test: number; }>; }' is missing the following properties from type 'Response': headers, ok, redirected, status, and 11 more.
What would be the simplest solution to get around this? Actually mock out the whole fetch object or other solution?
global.fetch = jest.fn(() =>Promise.resolve({json: () => Promise.resolve({ test: 100 }),}),)
Best Answer
You can tell TypeScript that you're defining global.fetch
as a Jest mock.
global.fetch = jest.fn(() =>Promise.resolve({json: () => Promise.resolve({ test: 100 }),}),) as jest.Mock;
I had some problems using the before approaches, here is how I work around that:
First my test code:
describe("Testing the Assets Service", () => {let fetchMock: any = undefined;beforeEach(() => {fetchMock = jest.spyOn(global, "fetch").mockImplementation(assetsFetchMock);});afterEach(() => {jest.restoreAllMocks();});test('Fetch has been called', () => {const baseUrl = "https://myurl.com"fetchAssets(baseUrl);expect(fetchMock).toHaveBeenCalled();expect(fetchMock).toHaveBeenCalledWith(baseUrl);}); });
The function fetchAssets call the fetch function with an specific url.
Now the function that mocks the fetch behavior:
export const assetsFetchMock = () => Promise.resolve({ok: true,status: 200,json: async () => clientAssets} as Response);
clientAssets is a object that I needed to return, you could replace it to the object or primitive you have to return.
you can also use this, which is similar to this answer https://stackoverflow.com/a/64819545/19334273
jest.spyOn(global, "fetch").mockImplementation( jest.fn(() => Promise.resolve({ json: () => Promise.resolve({ data: 100 }), }), ) as jest.Mock )
from this comment Simple fetch mock using Typescript and Jest
Worked on a fetch resolver that can be initialised for each test case. Hope this is useful. You can add custom methods like based on the need.
type Method = "get" | "options" | "post" | "put" | "patch" | "delete";// https://httpstat.usexport enum Status {OK = 200,Created = 201,Accepted = 202,NonAuthoritativeInformation = 203,NoContent = 204,ResetContent = 205,PartialContent = 206,MultipleChoices = 300,MovedPermanently = 301,Found = 302,SeeOther = 303,NotModified = 304,UseProxy = 305,Unused = 306,TemporaryRedirect = 307,PermanentRedirect = 308,BadRequest = 400,Unauthorized = 401,PaymentRequired = 402,Forbidden = 403,NotFound = 404,MethodNotAllowed = 405,NotAcceptable = 406,ProxyAuthenticationRequired = 407,RequestTimeout = 408,Conflict = 409,Gone = 410,LengthRequired = 411,PreconditionFailed = 412,RequestEntityTooLarge = 413,RequestURITooLong = 414,UnsupportedMediaType = 415,RequestedRangeNotSatisfiable = 416,ExpectationFailed = 417,Imateapot = 418,MisdirectedRequest = 421,UnprocessableEntity = 422,Locked = 423,TooEarly = 425,UpgradeRequired = 426,PreconditionRequired = 428,TooManyRequests = 429,RequestHeaderFieldsTooLarge = 431,UnavailableForLegalReasons = 451,InternalServerError = 500,NotImplemented = 501,BadGateway = 502,ServiceUnavailable = 503,GatewayTimeout = 504,HTTPVersionNotSupported = 505,VariantAlsoNegotiates = 506,InsufficientStorage = 507,NetworkAuthenticationRequired = 511,Webserverisreturninganunknownerror = 520,Connectiontimedout = 522,Atimeoutoccurred = 524}/*** Stub API request, response in test cases.* - should be initialized and destroyed within the context of a specific case.* - highly customizable** <pre>* describe("Fetch API", () => {* let fetchResolver!: FetchResolver;* beforeEach(() => {* fetchResolver = new FetchResolver();* });** it("should load api", async () => {* // stub* fetchResolver.stub( "http://localhost:8080/endpoint", "post", { id: 100 }, { created: true }, 200);* // fetch* fetch("http://localhost:8080/endpoint",* { method: "post", body: JSON.stringify({ id: 100 })}* ).then((response) => {* if (response.ok) {* response.text().then((text) => {* console.log(text); // { created: true }* expect(text).toBeEqual({ created: true });* });* }* });* });** afterEach(() => {* fetchResolver.clear();* });* });* </pre>** Even though jest executes tests in parallel jest instance,* We can't go wrong if stubs are cleaned after its use*/export class FetchResolver {private mocks: Map<string, Response> = new Map();constructor() {this.init();}public stub(uri: string,method: Method,payload: any,response: any,status: Status) {const finalRequest: { input: RequestInfo | URL; init?: RequestInit } = {input: uri,init: {method: method,body: JSON.stringify(payload)}};console.log(`mocking fetch :::\nrequest ${this.prettyPrint(finalRequest)} with \nresponse ${this.prettyPrint(response)} ans status ${status}`);this.mocks.set(JSON.stringify(finalRequest),new Response(JSON.stringify(response), { status: status }));}private prettyPrint(json: any) {return JSON.stringify(json, null, 2);}public clear() {this.mocks.clear();}private init() {jest.spyOn(global, "fetch").mockImplementation((input: RequestInfo | URL, init?: RequestInit) => {const request = {input,init};return new Promise((resolve, reject) => {let response = this.mocks.get(JSON.stringify(request));if (response) {resolve(response);} else {// rejecting here will hurt component initializationconsole.error(`mock not implemented :::\nrequest ${this.prettyPrint(request)}`);// return empty responseresolve(new Response("{}"));}});});}public static initialize() {let resolver = new FetchResolver();resolver.stub("http://localhost:8080/endpoint","post",{ id: 100 },{created: true},200);fetch("http://localhost:8080/endpoint", {method: "post",body: JSON.stringify({ id: 100 })}).then((response) => {if (response.ok) {response.text().then((text) => {console.log(text); // { created: true }});}});}}
https://gist.github.com/mukundhan94/102334a8ba9b84d93a1b5ba4b7838647