How to override a global jest mock in a specific test? - reactjs

I am using #testing-library/react v13.4.0 with jest v29.
I am mocking the services/ledger.ts module with services/__mocks__/ledger.ts. I'll focus on one method in this file, which is defined as follows:
// services/__mocks__/ledger.ts
export const getAccounts = jest.fn(() => mockGetAccounts);
I have a setupTests.tsx that runs as setupFilesAfterEnv with the following code:
// setupTests.tsx
jest.mock('services/ledger');
This works nicely as a global mock, but for certain tests I want to override this mock. In a test file, I have the following code:
// Account.test.tsx
import { mockGbpAccount } from 'testing';
import { getAccounts } from 'services/ledger';
...
describe('components: Account', () => {
// some other tests that don't have any mocks
it('should disable GBP withdraw button if user has a balance of 0', async () => {
(getAccounts as jest.Mock).mockImplementation(() => ({
account: [
mockBtcAccount,
{ ...mockGbpAccount, balance: { formatted: '0' } },
],
}));
await renderWithProviders(<Account accountId={mockGbpAccount.id} />);
await waitFor(() => {
expect(screen.getByTestId('withdraw-cta')).toBeDisabled();
});
});
});
The resulting balance is not 0 as expected, but the default balance I set in the global mock.
Weirdly, when I run this test alone with -t, and not the entire suite, it passes. The specific test also passes when it's the first test in the suite, but not the second+ test.
I have tried different ways of mocking globally, and mocking in the specific suite, but it always uses the global mock value.
How can I override a global mock for specific tests?

Related

Is it possible to mock functions outside of the test file for multiple tests to use?

Thanks in advance
Issue
I have some functions that need to be mocked in every test file. However, only in some of those files, do I actually care to evaluate or test those mock functions.
For example, let's say, that when my app renders, it immediately fetches some data In A.test.tsx I want to mock that fetch and verify that it was called.
But, in B.test.tsx, I still need to mock that fetch, but I don't care to verify that it was called.
Goal
I would like the setup to look something like this:
setup.ts
import * as SomeAPI from 'api';
const setup = () => {
jest.spyOn(SomeAPI, 'fetchData');
return { SomeAPI };
};
export default setup;
A.test.tsx
const { SomeAPI } = setup();
beforeEach(() => {
jest.clearAllMocks();
});
test('data should be fetched when app renders', async () => {
RenderWithProviders(<App />);
expect(SomeAPI.fetchData).toHaveBeenCalled();
});
B.test.tsx
setup(); // Don't destructure because I don't care to test the mock
beforeEach(() => {
jest.clearAllMocks();
});
test('test some other stuff', async () => {
// In this case, the fetchData still needs to be mocked<br>
// because the rest of the app depends on it
RenderWithProviders(<App />);
expect(someElement).toBeInTheDocument();
});
My Current Problem
My problem is that while I'm trying to attempt this way of returning mocked functions... If that mocked function is used more than once in the same test file, the jest.clearAllMocks() seems not to have an effect. I assume because the setup is happening outside of the test?
Is this possible to setup mocks outside of the test and only destructure them when needed?

RTL: Mock a custom React hook only for selected unit tests

I have a custom hook called useInitialSetup that returns a key-value dictionary object.
It's being consumed in App.tsx.
In App.test.tsx, I have a suite of unit tests written using Jest and React Testing Library.
Is it possible to mock useInitialSetup in only a selection of unit tests, but not all?
Here's what I have tried in App.test.tsx:
jest.mock('../../hooks/useInitialSetup', () => {
return jest.fn(() => ({
loading: mockLoading,
suspendedTrial: mockSuspendedTrial,
maxTrials: mockMaxTrials,
reachedMaxTrials: mockReachedMaxTrials,
}));
});
it('Unit test that uses a mocked version of useInitialSetup', () => {
render(<App/>)
...
})
it('Unit test that uses the real implementation of useInitialSetup', () => {
jest.dontMock('../../hooks/useInitialSetup')
render(<App/>)
...
})
Unit test #2 still uses the mocked version of useInitialSetup.
Here's what I ended up doing. This lets me mock the output of my useInitialSetup custom hook for this one selected unit test. Any subsequent unit tests will use the default implementation of this hook.
import * as useInitialSetup from '../../hooks/useInitialSetup';
it('Test', () => {
const spy = jest.spyOn(useInitialSetup, 'default');
spy.mockReturnValue({
loading: false,
reachedMaxTrials: true,
errorCheckingCanCreateInstance: false,
errorFetchingUser: false,
// #ts-ignore
suspendedTrial: mockSuspendedTrial,
resetChecks: () => {},
maxTrials: 1,
});
render(setupComponent());
expect(true).toBe(true) // or whatever assertion you want
spy.mockRestore();
})

Is there a way to test a mocked function then the actual in the same test file on Jest?

In the same test file, I need to test the actual redux action creator and then, later, if the same action creator was called from another function.
If I jest.mock I'm unable to test the actual. If I don't, I can't mock it later.
I've also tried jest.spyOn with mockImplementation but it doesn't seem to mock it.
// actionsOne.js
export const myActionOne = ({
type: 'MY_ACTION_ONE'
})
// test.js
import { dispatch } from './reduxStore.js'
import { myActionOne } from './actionsOne.js'
import { myActionTwo } from './actionsTwo.js'
jest.mock('./actionsOne.js', () => ({
myActionOne: jest.fn()
}))
test('expectation 1', () => {
// would pass if no mocking is in place. fail otherwise.
expect(dispatch(myActionOne())).toEqual({
type: 'MY_ACTION_ONE'
})
})
test('expectation 2', () => {
dispatch(myActionTwo()) // say this would also dispatch myActionOne somewhere
// would pass if mocking is in place. fail otherwise.
expect(myActionOne).toHaveBeenCalled()
})
Because myActionOne does a lot of stuff when dispatched, I wanted to test it once and for myActionTwo just check if it was called.
Any recommendation? Thanks!
Ok, I got it to work using jest.requireActual():
test('expectation 1', () => {
const { myActionOne: myActionOneActual } = jest.requireActual('./actionsOne.js')
expect(dispatch(myActionOneActual())).toEqual({
type: 'MY_ACTION_ONE'
})
})
expectation 1 passes because we are asserting with the actual function.
No changes to expectation 2, since it asserts with the mocked function.

integration tests - redux/react + nock.js

I have no clue how to find a way to write this integration test.
I am using enzyme for mocking react components, jest for testing and nock for mocking axios api calls.
So far I created test which simulate clicking on button and I would like to mock the api call.
In the internet there is no much help.
My test:
it('Should invoke clear action and clear the group', (done) => {
// GIVEN
const clearButtonComponent = wrapper.find('[id="123"]');
nock('http://localhost:8080')
.intercept('/path/api/brum/123/group', 'DELETE')
.reply(200, {
status: 200,
message: 'cleared',
});
const service = new myService();
// WHEN
clearButtonComponent.first().simulate('click');
const result = Promise.resolve(service.clearGroup(123));
// THEN
expect(result).toEqual({ x: 'x' }); // I know it's not what I expect
wrapper.update();
done();
});
async action redux:
export const clearGroup = id=> (dispatch, getState) => {
myService.clearGroup(id)
.then(() => {
return dispatch(getGroup(id))
});
};
method in myService:
clearGroup(id) {
return this._delete(`/${id}/group`);
}
of course path is more complex but my service extends base service which has this base url.
Can anybody tell me how to mock it to let code goes further?
It still complain that id is undefined - look like nock does not mock it.
I would drop nock (I try to only use it for testing clients these days) and mock myService with jest.
I don't use axios, so haven't used this, but it might do the trick.. https://github.com/knee-cola/jest-mock-axios.
Otherwise you could look at writing your own mock.. https://jestjs.io/docs/en/es6-class-mocks

Resetting the value of a mocked module in a Jest unit test

I'm trying to mock out methods in an imported module while testing a separate module. I'm able to successfully mock the imported module with ES2015 import syntax, but the mock stays consistent throughout the entire test and there are instances where I'd like to change the mock.
My files look like this
// ModuleA
import ModuleB from 'ModuleB'
// ... code
// TestCase
import ModuleA from 'ModuleA'
import ModuleB from 'ModuleB'
jest.mock('ModuleB', () => {
return {
value: true
}
}
describe('ModuleA', () => {
it('returns true', () => {
// This test expects ModuleB.value to return true
});
it('returns false', () => {
// This doesn't work
ModuleB.value = jest.fn(() => false)
// This doesn't work either
jest.mock('ModuleB', () => {
return {
value: false
}
});
// This test expects ModuleB.value to return false in order to pass
});
});
I essentailly need to separate mocks for ModuleB. In the past, I've been able to simply use var ModuleB = require('ModuleB'); instead of import and then call ModuleB.someMethodName = jest.fn() whenever needed. I'd like to use only ES2015 for these tests though, and using the pattern I just mentioned gives me a ModuleB is read-only error.
Use the requireActual method:
To ensure that a manual mock and its real implementation stay in sync, it might be useful to require the real module using require.requireActual(moduleName) in your manual mock and amending it with mock functions before exporting it.
For example:
const RealModule = require.requireActual('Form');
const MyModule = {
RealThing: RealModule.RealThing,
…add some mocks
};
References
jest.js Issue #1557: How to mock just the React components from a module exporting many things
jest.js Issue #1273: require.requireActual not returning the actual mock
Troubleshooting · Jest

Resources