Reputation: 3269
I have defined 2 JEST mocks. The problem I am getting. The first mock doesn't work but the second does.
import Helper from '../../test-helper'
import Storage from '@/storage'
import GuestContext from '@/auth/guest-context'
import UserContext from '@/auth/user-context'
// import LocalStorageGateway from '@/storage/local/local-storage-gateway'
const mockContextUpsert = jest.fn()
jest.mock('@/storage/local/local-storage-gateway', () => {
return jest.fn().mockImplementation((authContext) => {
return {
authContext,
context: {
upsert: mockContextUpsert
}
}
})
})
// import RemoteStorageGateway from '@/storage/remote/remote-storage-gateway'
const mockFetch = jest.fn()
jest.mock('@/storage/remote/remote-storage-gateway', () => {
return jest.fn().mockImplementation((authContext) => {
return {
authContext,
fetch: mockFetch
}
})
})
I have tried...
mockFetch
in the first mock instead of mockContentUpsert
(after of course moving the definition of mockFetch
up).The error I am getting is...
ReferenceError: mockContextUpsert is not defined
I cannot understand why the first mock doesn't work when the second mock works perfectly.
This will also work, adding the mock variables to the second declaration (not of any use as they're different classes but for reference)...
const mockContextUpsert = jest.fn()
const mockFetch = jest.fn()
jest.mock('@/storage/remote/remote-storage-gateway', () => {
return jest.fn().mockImplementation((authContext) => {
return {
authContext: authContext,
fetch: mockFetch,
context: {
upsert: mockContextUpsert
}
}
})
})
The classes being mocked here are almost identical.
UPDATE
Removing the reference to GuestContext()
[this is additional complication, has since been removed, and is confusing the actual problem the question is trying to ask]
Upvotes: 0
Views: 3738
Reputation: 3269
I have solved this eventually - The issue here wasn't with the mocks themselves but the class being mocked.
The LocalStorageGateway
class is used to create a private instance within an imported module Storage
, as follows...
const guestLocal = new LocalStorageGateway(new GuestContext())
This static context is causing the mocked constructor to execute before the variables are defined as Storage
is one of the first module imported.
There are several ways around this...
You can change this...
import Helper from '../../test-helper'
import Storage from '@/storage'
import GuestContext from '@/auth/guest-context'
import UserContext from '@/auth/user-context'
(insert mocks here)
to...
const mockContextUpsert = jest.fn()
import Helper from '../../test-helper'
import Storage from '@/storage'
import GuestContext from '@/auth/guest-context'
import UserContext from '@/auth/user-context'
for example (yuk?).
Alternatively you can wrap mockContextUpsert
up in a wrapping function (my earlier not-really-good-enough answer) -> This feels a bit tidier to me.
const mockContextUpsert = jest.fn()
jest.mock('@/storage/local/local-storage-gateway', () => {
return jest.fn().mockImplementation((authContext) => {
return {
authContext: authContext,
context: {
upsert: (cxt) => {
mockContextUpsert(cxt)
}
}
}
})
})
I could also make guestLocal
a function but I don't really want to do that and create new gateway instances every time I want to use it (It's why it's there in the first place). If Storage
was a class it would be instantiated in the constructor but it isn't and has no real need to be.
Thanks to @Teneff for his input on this, his answer got my brain looking at it the right way around. the variables are not hoisted was key - They work differently - I was operating on an incorrect understanding that anything called mockXXXX
would be hoisted up above the mock calls but they aren't they are just 'allowed'.
Upvotes: 1
Reputation: 32148
A quote from the jest documentation:
Jest will automatically hoist jest.mock calls to the top of the module (before any imports)
meaning that whenever you try to define the function being returned by the mock mockGuestContext
is not yet defined
What you can do is create an auto-mock
import localStorageGateway from '@/storage/local/local-storage-gateway';
jest.mock('@/storage/local/local-storage-gateway');
// and since localStorageGateway is a function
// jest will automatically create jest.fn() for it
// so you will be able to create authContext
const authContext = new GuestContext();
// and use it within the return value
localStorageGateway.mockReturnValue({
authContext,
context: {
upsert: jest.fn(),
}
})
Upvotes: 0