I am writing tests with jest in which I want to spy on some lodash functions, that I import separately in the module (opposed to importing the whole lodash module as _
), e.g.
/** matrix.js **/
import shuffle from 'lodash/shuffle'
import pick from 'lodash/pick'
// ...
/**
* Shuffles the order of the rows in the matrix. If a column/variable name
* is specified, only the rows in this column are shuffled.
*
* @export
* @param {array} matrix The matrix to be shuffled
* @param {array} columns Array containing the variable/column to be shuffled
* @returns {array}
*/
export function shuffleVert (matrix, columns) {
if (typeof (columns) === 'undefined' || (isArray(columns) && columns.length === 0)) {
return shuffle(matrix)
} else if (!isArray(columns)) {
throw new TypeError('Invalid argument for columns specified to shuffleVert. Expects an array containing column names')
} else {
let grouped = unstack(matrix)
let cols = pick(grouped, columns)
cols = Object.entries(cols).reduce((prev, [key, values]) => {
prev[key] = shuffle(values)
return prev
}, {})
return stack({ ...grouped, ...cols })
}
The shuffleVert
function shuffles all rows of a matrix, or only those of the specified columns. Because it is difficult to test functions with a random output, to my knowledge, I just want to test if the shuffle and pick function of lodash have been called inside the tested function.
I currently have implemented a working spy procedure in my test module, but I don't think it's conventional or efficient, and I just think there must be a better way to do this...
/* matrix.test.js */
import {
shuffleVert,
} from 'matrix'
/** Generate a mock functions to spy on lodash */
const mockShuffle = jest.fn()
jest.mock('lodash/shuffle', () => a => {
const shuffle = jest.requireActual('lodash/shuffle')
mockShuffle()
return shuffle(a)
})
const mockPick = jest.fn()
jest.mock('lodash/pick', () => (a, b) => {
const pick = jest.requireActual('lodash/pick')
mockPick()
return pick(a, b)
})
describe('reverseRows', () => {
let srcMatrix
beforeEach(() => {
srcMatrix = [
{ number: 1, word: 'one' },
{ number: 2, word: 'two' },
{ number: 3, word: 'three' }
]
mockShuffle.mockClear()
mockPick.mockClear()
})
it('should shuffle the rows of the entire matrix with no argument for columns', () => {
shuffleVert(srcMatrix)
// 2 is weird, but seems correct.
// It appears the shuffle function calls itself recursively
expect(mockShuffle).toHaveBeenCalledTimes(2)
})
it('should only shuffle the rows of columns that were specified', () => {
shuffleVert(srcMatrix, ['word'])
expect(mockShuffle).toHaveBeenCalledTimes(2)
expect(mockPick).toHaveBeenCalledTimes(2)
})
})
I know jest has a spyOn
functionality, but that only appears to work on object methods, thus
import * as lodash from 'lodash'
const shuffleSpy = jest.spyOn(lodash, 'shuffle')
results in the error Cannot spyOn on a primitive value; undefined given
What is generally the best way in jest to spy on methods of a module? Is there a better implementation for doing what I'm trying to achieve? Or is this already the way to go?
Please try this and let me know if it works for you
import { shuffleVert } from 'matrix';
const shuffleVertRef = {
shuffleVert
};
let spyShuffleVert;
beforeEach(() => {
spyShuffleVert = jest.spyOn(shuffleVertRef, "shuffleVert");
}
afterEach(() => {
jest.clearAllMocks();
}
it('should shuffle the rows of the entire matrix with no argument for columns', () = {
let srcMatrix = [
{ number: 1, word: 'one' },
{ number: 2, word: 'two' },
{ number: 3, word: 'three' }
];
shuffleVert(srcMatrix);
expect(spyShuffleVert).toHaveBeenCalledTimes(2);
}
Then if you want to check any other function execution times, just add them to a/that constant and spyOn
them below.
I too don't have a full understanding of this, but from the little I read, the latest versions of JavaScript have this principle called "single source of truth" which is required for some things to work properly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With