When running a Jest test, a variable outside the function is undefined - javascript

This is my dev code below.
import { proxyManager } from '../../proxy'
const proxy = proxyManager.get()
// proxy is expected to be an object like this:
// { post: () => {}, get: () => {} }
export const updateNamePrefixToAll = ({ origPrefix, prefix, primDBId }) => {
return proxy?.post(
`/model/tables/physicalTables/tablePrefixes/batch?primaryDataSourceId=${primDBId}&oldTablePrefix=${encodeURL(
origPrefix
)}&newTablePrefix=${encodeURL(prefix)}`
)
}
The test code is shown below
import {
updateNamePrefixToAll,
} from '#tableList/service'
describe('service.ts', () => {
it('', () => {
updateNamePrefixToAll({
origPrefix: 'origPrefix1',
prefix: 'prefix1',
primDBId: 'primDBId1',
})
})
})
After running the test, there is an error.
ReferenceError: proxy is not defined
I have solved this error by mocking the module below
import { proxyManager } from '../../proxy'
Beside this, how can I solve this issue?

Related

Express test using JEST and ESM give me error "TypeError: Cannot assign to read only property 'sum' of object '[object Module]'"

i tried to using jest for mocking imported function but i got this error TypeError: Assignment to constant variable. or TypeError: Cannot assign to read only property 'sum' of object '[object Module]', i expect that i got return value that i mocked in this test
Attempt 1
import { jest } from '#jest/globals'
import * as util from "./util.js"
it("TypeError: Cannot assign to read only property 'sum' of object '[object Module]'", () => {
jest.spyOn(util, "sum").mockImplementation(() => { return 2 })
expect(sum(1, 2)).toBe(2);
})
Attempt 2
import { jest } from '#jest/globals'
import { sum } from './util.js'
it("TypeError: Cannot assign to read only property 'sum' of object '[object Module]'", () => {
jest.mock("./util.js", () => ({
__esModule: true,
sum: jest.fn().mockReturnValue(2),
}));
expect(sum(1, 2)).toBe(2);
})
Attempt 3
import { jest } from '#jest/globals'
import { sum } from "./util.js"
it("TypeError: Assignment to constant variable.", () => {
sum = jest.fn(() => { return 2 })
expect(sum(1, 2)).toBe(2);
})
i'm following jest documentation https://jestjs.io/docs/ecmascript-modules to setup my config
package.json
{
"type": "module",
"scripts": {
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
},
}
jest.config.js
module.exports = async () => {
return {
verbose: true,
transform: {}
};
};
i created this repo for reproduction https://github.com/fei1990a/jest-esm/tree/main
Thankyou for any help
Here is the workaround I came up with.
import { jest } from "#jest/globals";
beforeAll(() => {
jest.unstable_mockModule("./util.js", () => ({
sum: jest.fn(() => { return 2 }),
}));
});
afterAll(() => {
jest.clearAllMocks();
});
test("should sum value be 2", async () => {
const { sum } = await import("./util.js");
expect(sum(1, 2)).toBe(2);
});
Reference : https://github.com/facebook/jest/issues/10025

How do you mock a named exports constructor and functions of an external library with Jest?

I have seen similar questions but nothing I have seen in the documentation or stackoverflow describes what I am trying to do. I am new to javascript and just started using jest, I have read through the jest documentation but I have not seen an example that mocks a named export of an external library. The library I am trying to mock is rate-limiter-flexible. I want to mock the named export RateLimiterRedis. I need to mock a couple of RateLimiterRedis functions, including get, consume, and delete.
For example when I mocked a function from redis all I had to do was:
import redis from 'redis';
jest.mock('redis', () => {
return { createClient: jest.fn()};
});
When I try:
jest.mock('rate-limiter-flexible', () => {
return jest.fn().mockImplementation(() => {
return { RateLimiterRedis: { get: mockGet } }
});
});
I get: TypeError: _rateLimiterFlexible.RateLimiterRedis is not a constructor
When I try:
jest.mock('rate-limiter-flexible', () => {
return { RateLimiterRedis: () => {}}
});
I get: TypeError: limiter.get is not a function
So it recognizes the constructor but I need to add the functions.
I have tried:
jest.mock('rate-limiter-flexible', () => {
return { RateLimiterRedis: () => {
return jest.fn().mockImplementation(() => {
return {
get: mockGet
}
})
},
}
});
This also gives: TypeError: limiter.get is not a function
This is in my file I am trying to test:
const limiter = new RateLimiterRedis(opts);
I have also tried doMocking the named export itself (since mock hoists itself to the top) to no success
My question boils down to how can I mock a constructor of a class and that classes functions with jest, when that class is a named export of an external library?
Edit:
mockGets definition:
const mockIpAndUrl ={
consumedPoints:1
};
const mockGet = jest.fn().mockImplementation(() => {
return mockIpAndUrl;
})
This does not work:
const mockIpAndUrl ={
consumedPoints:1
};
const mockGet = jest.fn().mockImplementation(() => {
return mockIpAndUrl;
})
jest.mock('rate-limiter-flexible', () => {
return{
RateLimiterRedis: jest.fn().mockImplementation(() => {
return { get : mockGet};
})
}
});
TypeError: limiter.get is not a function
However, this does:
jest.mock('rate-limiter-flexible', () => {
return{
RateLimiterRedis: jest.fn().mockImplementation(() => {
return { get : jest.fn().mockImplementation(() => {
return mockIpAndUrl;
})};
})
}
});
This is the documentation I was referring to:
https://jestjs.io/docs/en/es6-class-mocks#calling-jestmockdocsenjest-objectjestmockmodulename-factory-options-with-the-module-factory-parameter
This lead me to believe I could use mockGet
The export of rate-limiter-flexible is an object that is supposed to have RateLimiterRedis function that returns an instance that has get method.
"I have tried" snippet is wrong because it makes RateLimiterRedis function to return another function, and RateLimiterRedis itself isn't a spy so it cannot be asserted.
It should be:
jest.mock('rate-limiter-flexible', () => {
return {
RateLimiterRedis: jest.fn().mockImplementation(() => ({
get: mockGet
})
};
});
If RateLimiterRedis is instantiated in top-level imports, mockGet cannot be assigned before it's accessed inside a mock. It can be exported as a part of mocked module:
jest.mock('rate-limiter-flexible', () => {
const mockRateLimiterRedisGet = jest.fn();
return {
mockRateLimiterRedisGet,
RateLimiterRedis: jest.fn().mockImplementation(() => ({
get: mockRateLimiterRedisGet
})
};
});
This allows to import mockRateLimiterRedisGet from rate-limiter-flexible in a test and use for dynamically mocked values and assertions:
mockRateLimiterRedisGet.mockReturnValue(...);
...
expect(mockRateLimiterRedisGet).toBeCalled();

How to jest spyOn a commonJS default export

Is it possible to jest.spyOn a default export so that I can call the mockImplementation method to change what the function does before each test?
// code.js
module.exports = () => {
// some irrelevant code I want to rewrite with a mock
};
// test
const code = require('./code.js');
const mockCode = jest.spyOn(code, 'default'); // this line doesn't work with the error: "Cannot spy the default property because it is not a function; undefined given instead"
it('some test', async () => {
mockCode.mockImplementationOnce(() => { console.log('test') });
});
I've also tried to use jest.mock() unsuccessfully:
const code = require('./code');
jest.mock('./code',
() => () => {
console.log('test');
}
);
it('some test', async () => {
code.mockImplementationOnce(() => { console.log('test2') }); // error
});

Calling 'created' method inside Jest tests on Vue.js

I'm testing my Activity.vue component on Vue.js with Jest.
My Activity.vue component calls the initializeTable() method inside created() in order to initialize my tableRows property.
The problem is, when I run my tests, Jest does not call 'created()' method and, for that reason, it does not initialize my tableRows property, then the test says that my tableRows property is equal to [] (original state).
How could I call created() method when I'm testing with Jest?
I've already tried to call it explicitly inside the test (i.e. wrapper.vm.$created()), but to no avail.
Would anyone know how to solve it?
I give my code below.
Thank you in advance.
Activity.spec.js
import { shallowMount } from '#vue/test-utils'
import Activity from '#/views/Activity.vue'
import api from '#/assets/js/api'
const expectedTableRows = [...]
const $t = () => {}
api.getActivity = jest.fn(
() => Promise.resolve({
data: [...]
})
)
describe('Activity.vue', () => {
it('renders vm.tableRows in created()', () => {
const wrapper = shallowMount(Activity, {
mocks: { $t }
})
// wrapper.vm.$created()
expect(wrapper.vm.tableRows).toEqual(expectedTableRows)
})
})
Activity.vue
import api from '#/assets/js/api'
export default {
data () {
return {
tableRows: []
}
},
created () {
this.initializeTable()
},
methods: {
initializeTable () {
this.loading = true
api.getActivity().then(response => {
this.tableRows = response.data
}).catch(error => {
this.errorBackend = error
}).finally(() => {
this.loading = false
})
}
}
}
EDIT 1: Solution
As Husam Ibrahim described, the function is asynchronous, because of that, I needed to refactor the test using the following tips and now I was able to fix it.
I give the correct code below:
import flushPromises from 'flush-promises' // <--
...
describe('Activity.vue', () => {
it('renders wm.tableRows after created()', async () => { // <--
const wrapper = shallowMount(Activity, {
mocks: { $t }
})
await flushPromises() // <--
expect(wrapper.vm.tableRows).toEqual(expectedTableRows)
})
})

sinon mock not catching calls

I am having a hard time understanding what I am doing wrong.
I have a JS class as such:
export default class A {
constructor(repository) {
this._repository = repository;
}
async process(date) {
// ...
this._repository.writeToTable(entry);
}
}
and I am attempting to write a test that mocks the repository using sinon.mock
This is what I have so far:
describe('A', () => {
describe('#process(date)', () => {
it('should work', async () => {
const repository = { writeToTable: () => {} };
const mock = sinon.mock(repository);
const a = new A(repository);
await a.process('2017-06-16');
mock.expects('writeToTable').once();
mock.verify();
});
});
});
but it always fails saying that
ExpectationError: Expected writeToTable([...]) once (never called)
I've checked (added a console.log) and it is calling the object I defined on the test.
I ran this locally and read the documentation on sinonjs.org and you seem to be doing everything right.
I tried re-writing your example using a spy and ended up with something like this to get a passing test:
import sinon from "sinon";
import { expect } from "chai";
import A from "./index.js";
describe("A", () => {
describe("#process(date)", () => {
it("should work", async () => {
const repository = { writeToTable: sinon.spy() };
const a = new A(repository);
await a.process("2017-06-16");
expect(repository.writeToTable.calledOnce).to.be.true;
});
});
});

Categories