ignore setup mock for one file - node.js

I have a decorator that I use on a lot of my methods. To not have to mock it each time, I have added a mock on jest.setup.js:
jest.mock('src/something', () => {
someMethod: jest.fn.mockImplementation(/*some implementation*/)
})
This works fine, but now I want to unit test this one method (the someMethod in this example) and I can't, since it brings up the mock. How can I ignore this mock for only this file/test?

You can use jest.unmock(moduleName) in the test file of a module. So that your module under test will use the real module rather than the mocked version.
Let's see an example:
./src/stackoverflow/73129547/something.ts:
export const someMethod = () => 1;
This is the module we want to mock.
./jest.setup.js:
jest.setTimeout(5 * 1000);
jest.mock('./stackoverflow/73129547/something', () => ({
someMethod: jest.fn().mockReturnValue(0),
}));
jest.config.js:
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['./jest.setup.js'],
};
We set up the mock using jest.mock() in the jest.setup.js file. So that every module under test will use the mocked version of this module.
Suppose our project has two modules: a.ts and b.ts, they use the someMethod exported from the something module.
./src/stackoverflow/73129547/a.ts:
import { someMethod } from './something';
export const a = someMethod;
./src/stackoverflow/73129547/b.ts:
import { someMethod } from './something';
export const b = someMethod;
The test suites of a and b modules:
./src/stackoverflow/73129547/a.test.ts:
import { a } from './a';
describe('73129547 - a', () => {
test('should pass', () => {
expect(a()).toBe(0);
});
});
./src/stackoverflow/73129547/b.test.ts:
import { b } from "./b";
describe('73129547 - b', () => {
test('should pass', () => {
expect(b()).toBe(0);
});
});
Test result:
PASS stackoverflow/73129547/b.test.ts
PASS stackoverflow/73129547/a.test.ts (9.829 s)
Test Suites: 2 passed, 2 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 10.681 s
As you can see the expected execution result. Both the a and b modules use the mocked someMethod which has a mock return value: 0.
Now, we want to test the someMethod of the something module, we should test the real someMethod rather than the mocked one. Test mock implementations make no sense.
./src/stackoverflow/73129547/something.test.ts:
import { someMethod } from './something';
jest.unmock('./something');
describe('73129547 - something', () => {
test('should pass', () => {
expect(someMethod()).toBe(1);
});
});
Test result:
PASS stackoverflow/73129547/a.test.ts
PASS stackoverflow/73129547/b.test.ts
PASS stackoverflow/73129547/something.test.ts
Test Suites: 3 passed, 3 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 1.955 s, estimated 14 s
As you can see, the return value of real someMethod is 1. jest.unmock(moduleName) will unmock the module and return the real module.

Related

How to test a function from a svelte component with jest?

There is a lot of documentation on the internet to test svelte component with jest, calling render functions and simulating browser events. This is nice, but how can I test a function inside a svelte component?
mycompoment.svelte
<script>
function veryComplicated(foo) {
...
}
</script>
<div>...</div>
mycomponent.test.js
import { veryComplicated } from "./mycomponent.svelte"
test('it works', async () => {
expect(vercomplicated("foo").toBe("bar"))
})
jest
FAIL src/mycomponent.test.ts
● Test suite failed to run
src/mycomponent.test.ts:1:10 - error TS2614: Module '"*.svelte"' has no exported member 'veryComplicated'. Did you mean to use 'import veryComplicated from "*.svelte"' instead?
1 import { veryComplicated } from "./mycomponent.svelte"
~~~~~~~~~~~~~~~
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 1.697 s
Ran all test suites.
Adding export before the veryComplicated definition does not help.
How can I test the veryComplicated function?
You can export a function using a module context script block.
<script context="module">
export veryComplicated() {
// ...
}
</script>
<div>...</div>
Then you can do import { veryComplicated } from './mycomponent.svelte' as you were originally trying to do.
https://svelte.dev/tutorial/module-exports
Found it. I had to call render
mycomponent.test.js
import { render } from '#testing-library/svelte'
import MyComponent from "./mycomponent.svelte"
test('it works', async () => {
const component = render(MyComponent)
expect(component.verComplicated("foo").toBe("bar")
})
And it is needed to export the veryComplicated function.

Jest spiedOn method is not called, but called once change the test case order

The Spied on code is not called when the test is at the second position. (or a second render) and test case fails
Test case passes if the test case is at the first position(or on first render).
Using a very basic create-react-app OOTB example and simplifying it even more for a MCVE:
MyModule.js
import React from 'react';
import someClass from './someClass';
function App() {
someClass.track("someevent");
return null;
}
export default App;
someClass.js
class SomeClass {
constructor() {
this.someProp = null;
}
getSatellite() {
return {
track: () => {}
};
}
track(someProp) {
///THIS BELOW IF CLAUSE IS THE PROBLEM
if (this.someProp === someProp) {
return;
} else {
this.someProp = someProp;
}
///////////////////////
this.getSatellite().track('someevent');
}
}
const instance = new SomeClass();
export default instance;
App.js
import React from 'react';
import MyModule from './MyModule'
function App() {
return (
<div className="App">
<MyModule />
</div>
);
}
export default App;
App.test.js
import React from 'react';
import { render } from '#testing-library/react';
import App from './App';
import someClass from './someClass';
test('renders learn react link', () => {
render(<App />);
});
// it works if this test case is first one, weird :-|
test('renders class', () => {
const track = jest.fn();
jest.spyOn(someClass, 'getSatellite').mockImplementation(()=>{
console.log('here i am');
return {
track
}
})
render(<App />);
expect(track).toHaveBeenCalledTimes(1);
});
Output:
✓ renders learn react link (17ms)
✕ renders class (5ms)
● renders class
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0
18 | })
19 | render(<App />);
> 20 | expect(track).toHaveBeenCalledTimes(1);
| ^
21 | });
22 |
at Object.<anonymous> (src/App.test.js:20:17)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 10.472s
Let me know if you need anything in the comments.
In the first render(<App />) the someProp of someClass is being set as someevent.
Now in the next render I am only mocking the function call, but not resetting the someProp. That's why the if (this.someProp === someProp) is taking effect.
So I need to reset the someProp to another value or null and it will work fine.

Jest automatic-mocks: funny example outcome

this refers to the facebook example tutorials
// utils.js
// Copyright 2004-present Facebook. All Rights Reserved.
export default {
authorize: () => 'token',
isAuthorized: (secret) => secret === 'wizard',
};
below is the test file. Instead of adding auto mock at the config file, I added inside the code to show the differences.
import utils from './utils';
jest.enableAutomock();
test('implementation created by automock', () => {
expect(utils.authorize('wizzard')).toBeUndefined();
expect(utils.isAuthorized()).toBeUndefined();
});
outcome:
TypeError: Cannot read property 'default' of undefined
6 |
7 | test('implementation created by automock', () => {
> 8 | expect(utils.authorize('wizzard')).toBeUndefined();
| ^
9 | expect(utils.isAuthorized()).toBeUndefined();
10 | });
11 |
at Object.utils (__tests__/example/automatic-mocks/genMockFromModule.test.js:8:10)
Why is that? it happens to another file automock.test.js. The error message is the same.
// Copyright 2004-present Facebook. All Rights Reserved.
import utils from './utils';
jest.enableAutomock();
test('if utils are mocked', () => {
expect(utils.authorize.mock).toBeTruthy();
expect(utils.isAuthorized.mock).toBeTruthy();
});
test('mocked implementation', () => {
utils.authorize.mockReturnValue('mocked_token');
utils.isAuthorized.mockReturnValue(true);
expect(utils.authorize()).toBe('mocked_token');
expect(utils.isAuthorized('not_wizard')).toBeTruthy();
});
Below example works for me, I use jestjs with typescript and ts-jest.
the docs say:
Note: this method was previously called autoMockOn. When using babel-jest, calls to enableAutomock will automatically be hoisted to the top of the code block. Use autoMockOn if you want to explicitly avoid this behavior.
utils.ts:
const utils = {
getJSON: data => JSON.stringify(data),
authorize: () => 'token',
isAuthorized: secret => secret === 'wizard'
};
export default utils;
utils.spec.ts:
jest.enableAutomock();
import utils from './utils';
describe('automatic mocks test suites', () => {
it('should mock all methods of utils', () => {
expect((utils.getJSON as jest.Mock).mock).toBeTruthy();
expect(jest.isMockFunction(utils.authorize)).toBeTruthy();
expect(jest.isMockFunction(utils.isAuthorized)).toBeTruthy();
});
test('implementation created by automock', () => {
expect(utils.authorize()).toBeUndefined();
expect(utils.isAuthorized('wizard')).toBeUndefined();
});
it('mocked implementation', () => {
(utils.getJSON as jest.Mock).mockReturnValue(123);
expect(utils.getJSON({ name: 'test' })).toBe(123);
});
});
Unit test result:
PASS src/automatic-mocks/utils.spec.ts (17.906s)
automatic mocks test suites
✓ should mock all methods of utils (4ms)
✓ implementation created by automock (2ms)
✓ mocked implementation (1ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 22.923s, estimated 23s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/automatic-mocks

Using react-hooks-testing-library with jest.spyOn - spy is not called

I am having an issue setting up a unit test to determine that a function is called with the correct arguments. useAHook returns function foo which calls function bar. The code looks like this
//myModule.js
export const useAHook = (arg1, arg2) => {
const foo = useCallback(() => {
bar(arg1, arg2);
}, [arg1, arg2]);
return foo;
}
export const bar = (a, b) => {
//does some stuff with a and b
}
I am trying to unit test this code using renderHook and jest.spyOn. I want to confirm that calling function foo results in bar being called with the correct arguments. My unit test looks like this
//myModule.spec.js
import * as myModule from './myModule.js'
it('should call foo with correct arguments', () => {
const spy = jest.spyOn(myModule, 'bar');
const { result } = renderHook(() => myModule.useAHook('blah', 1234));
const useAHookFunc = result.current;
useAHookFunc();
// fails, spy is not called
expect(spy).toBeCalledWith('blah', 1234);
});
The result is that the test fails saying that spy is never called. Am I doing something wrong here or using either tool incorrectly?
This line:
import * as myModule from './myModule.js'
...imports the module bindings for myModule.js into myModule.
Then this line:
const spy = jest.spyOn(myModule, 'bar');
...wraps the module export for bar in a spy...
...but the spy never gets called because useAHook doesn't call the module export for bar, it just calls bar directly.
If you modify useAHook to call the module export for bar then the spy will get called.
There are a couple of ways to do that.
You can move bar into its own module...
...or you can import the module bindings for myModule.js so you can call the module export for bar:
import { useCallback } from 'react';
import * as myModule from './myModule'; // <= import the module bindings
export const useAHook = (arg1, arg2) => {
const foo = useCallback(() => {
myModule.bar(arg1, arg2); // <= call the module export for bar
}, [arg1, arg2]);
return foo;
}
export const bar = (a, b) => {
//does some stuff with a and b
}
import * as myModule from './...'
const mockedFn = spyOn(myModule, "useSomething")
I managed to spy on the hook export method (using import * as), then inject a mock function into the implementation:
import * as useThingHook from 'useThing'
it('a test', () => {
const methodMock = jest.fn()
jest.spyOn(useThingHook, 'usething').mockImplementation(() => ({
method: methodMock
}))
act()
expect(methodMock).toHaveBeenCalled()
})

Cannot mock filesystem in nodejs unit tests

Overview
I have a simple module written in nodejs that uses fs-extra package to test if a file exists. The module throws when the path exists and proceed to next procedure otherwise. Here is the source file:
// - main.js -
import fs from 'fs-extra'
export default async (pathName) => {
// Do not proceed if path already exists.
if (await fs.pathExists(projectPath)) {
throw new Error(chalk.red.bold(`${projectPath} already exists`))
}
// more logic here
}
I want to write a unit test that tests the bellow logic:
If filepath exists, we expect to throw an error
I don't want to mess up with the real filesystem -in case my code contains some nasty bug that could destroy it- so I went to an alternative solution, mocking the filesystem using mock-fs. Here is the spec file:
// - main.js spec file -
import mainFunction from '../main'
import mockfs from 'mock-fs'
describe('test main function', () => {
beforeEach(() => {
mockfs({
home: {
user: {
dummy: {}
}
}
})
})
test('expect to throw', async () => {
await mainFunction('/home/user/dummy')
})
afterEach(() => {
mockfs.restore()
})
})
What's the problem?
Every time I run the test, the main function does not throw. This happens because mockfs fake-filesystem was declared in the spec file, so the fs module in main source file does not know for the mockfs fake-filesystem and checks the real one. By the time that I do not have a folder named /home/user/dummy in my real filesystem the check always fails.
Expected behaviour
mainFunction in spec file should throw
Actual behaviour
mainFunction in spec file DOES NOT throw
Other info
I guess that I can turn this unit test into an integration test. But I do not want to. Is there any fix for this? Do I have to use another packages?
My test suit is Jest 22.3.0.
After some search, I found the appropriate way to unit test the branch. We really do not have to use the mock-fs module. We just have to mock pathExists method of fs-extra module to return one time the value false and one time the value true. Bellow, I post a working version of my spec file:
import mainFunction from '../main'
require('fs-extra').pathExists = jest.fn().mockReturnValueOnce(false).mockReturnValueOnce(true)
describe('test main function', () => {
beforeEach(() => {
jest.clearAllMocks()
})
test('expect to not throw', async () => {
await expect(mainFunction('/dummy/path/does/not/matter')).resolves
})
test('expect to throw', async () => {
await expect(mainFunction('/dummy/path/does/not/matter')).rejects.toBeInstanceOf(Error)
})
})

Resources