I have the http class:
class http {
constructor() {}
public async request(url: string, options: RequestInit): Promise<Response> {
const response = await fetch(`${url}`, options)
return response
}
public async get(url: string): Promise<Response> {
return this.request(url, { method: 'GET' })
}
public async post(url: string, data?: any): Promise<Response> {
return this.request(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
}
public async put(url: string, data?: any): Promise<Response> {
return this.request(url, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
}
public async delete(url: string): Promise<Response> {
return this.request(url, { method: 'DELETE' })
}
}
export default http
Then i use the http class inside Core as an injected dependency
export class Core {
public http: http
constructor(http: http) {
this.http = http
}
public async getUserDomainNameEntry(
username: string,
domainUrl: string,
): Promise<IDomainNameEntry | undefined> {
const response = await this.http.get(
`${domainUrl}/api/v1/dns/search/username/${username}`,
)
if (response.status === 404 || !response.ok) {
console.log(response)
return undefined
}
const dnsEntry: IDomainNameEntry = await response.json()
return dnsEntry
}
}
This is my jest test:
import { Core } from '.'
import http from '../http'
it('Domain Name Entry Test', async () => {
http.prototype.get = jest.fn(async (_url: string) =>
Promise.resolve({
ok: true,
status: 200,
json: async () => ({ name: 'javierhersan.stw', urls: [], ips: [] }),
} as Response),
)
const core = new Core(new http())
const domainNameEntry = await core.getUserDomainNameEntry(
'javierhersan.stw',
'http://localhost:3000',
)
expect(domainNameEntry).toBeDefined()
if (domainNameEntry) {
expect(domainNameEntry).toHaveProperty('name')
expect(domainNameEntry).toHaveProperty('urls')
expect(domainNameEntry).toHaveProperty('ips')
}
})
Error
TypeError: fetch failed
3 |
4 | public async request(url: string, options: RequestInit): Promise<Response> {
> 5 | const response = await fetch(`${url}`, options)
| ^
6 | return response
7 | }
8 |
at http.request (src/http/index.ts:5:20)
at Core.getUserDomainNameEntry (src/core/index.ts:172:20)
at Object.<anonymous> (src/core/index.test.ts:116:27)
Why is my mock method not overriding my http get original method. What is the best way of mocking an object method and injecting it as a dependency with jest? I have tested several ways and is not working is always calling the original get method, why?
I have the http class:
class http {
constructor() {}
public async request(url: string, options: RequestInit): Promise<Response> {
const response = await fetch(`${url}`, options)
return response
}
public async get(url: string): Promise<Response> {
return this.request(url, { method: 'GET' })
}
public async post(url: string, data?: any): Promise<Response> {
return this.request(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
}
public async put(url: string, data?: any): Promise<Response> {
return this.request(url, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
}
public async delete(url: string): Promise<Response> {
return this.request(url, { method: 'DELETE' })
}
}
export default http
Then i use the http class inside Core as an injected dependency
export class Core {
public http: http
constructor(http: http) {
this.http = http
}
public async getUserDomainNameEntry(
username: string,
domainUrl: string,
): Promise<IDomainNameEntry | undefined> {
const response = await this.http.get(
`${domainUrl}/api/v1/dns/search/username/${username}`,
)
if (response.status === 404 || !response.ok) {
console.log(response)
return undefined
}
const dnsEntry: IDomainNameEntry = await response.json()
return dnsEntry
}
}
This is my jest test:
import { Core } from '.'
import http from '../http'
it('Domain Name Entry Test', async () => {
http.prototype.get = jest.fn(async (_url: string) =>
Promise.resolve({
ok: true,
status: 200,
json: async () => ({ name: 'javierhersan.stw', urls: [], ips: [] }),
} as Response),
)
const core = new Core(new http())
const domainNameEntry = await core.getUserDomainNameEntry(
'javierhersan.stw',
'http://localhost:3000',
)
expect(domainNameEntry).toBeDefined()
if (domainNameEntry) {
expect(domainNameEntry).toHaveProperty('name')
expect(domainNameEntry).toHaveProperty('urls')
expect(domainNameEntry).toHaveProperty('ips')
}
})
Error
TypeError: fetch failed
3 |
4 | public async request(url: string, options: RequestInit): Promise<Response> {
> 5 | const response = await fetch(`${url}`, options)
| ^
6 | return response
7 | }
8 |
at http.request (src/http/index.ts:5:20)
at Core.getUserDomainNameEntry (src/core/index.ts:172:20)
at Object.<anonymous> (src/core/index.test.ts:116:27)
Why is my mock method not overriding my http get original method. What is the best way of mocking an object method and injecting it as a dependency with jest? I have tested several ways and is not working is always calling the original get method, why?
Share Improve this question asked Feb 2 at 16:39 javierhersanjavierhersan 234 bronze badges 1 |1 Answer
Reset to default 1You should use jest mocks to mock your http class instead of modifying the prototype:
https://jestjs.io/docs/es6-class-mocks#the-4-ways-to-create-an-es6-class-mock
import { Core } from '.'
import http from '../http'
jest.mock('../http', () => {
return jest.fn().mockImplementation(() => ({
async get(url) {
return { ok: true }
}
}))
})
But I don't think this is a good test to do.
In a unit test you want to test your components in isolation. If you're testing Core
, you want to test just Core
and mock any dependencies it has. In this case Http
. A separate unit test will take care of that.
Another way to say this: In this test we don't care what Http
does internally. We only care about what Core
does internally and how it interacts with Http
.
We do this my mocking your Http
instance (not class) and checking that Core
calls Http
correctly.
import { Core } from '.'
it('Domain Name Entry Test', async () => {
const mockResponse = { name: 'javierhersan.stw', urls: [], ips: [] }
const mockGet = jest.fn().mockImplementation(async url => ({
ok: true,
status: 200,
json: async () => mockResponse,
}))
const mockHttp = {
get: mockGet
}
const core = new Core(mockHttp)
const domainNameEntry = await core.getUserDomainNameEntry(
'javierhersan.stw',
'http://localhost:3000',
)
expect(mockGet).toBeCalledWith('http://localhost:3000/api/v1/dns/search/username/javierhersan.stw')
expect(domainNameEntry).toEqual(mockResponse)
})
getUserDomainNameEntry
directly callshttp.request
, nothttp.get
as it does in the code you've shown us – Bergi Commented Feb 2 at 19:25