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?
You 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