Reputation: 4782
I've manage to test Vuex getters that are isolated from other code. I'm now facing some issues when a getter depends on other getters, see the following example:
getters.js
export const getters = {
getFoo(state) => prefix {
return `${prefix}: ${state.name}`;
},
getFancyNames(state, getters) {
return [
getters.getFoo('foo'),
getters.getFoo('bar')
]
}
}
getters.spec.js
import { getters } = './getters';
const state = {
name: 'stackoverflow'
};
describe('getFoo', () => {
it('return name with prefix', () => {
expect(getters.getFoo(state)('name')).toBe('name: stackoverflow');
});
});
describe('getFancyNames', () => {
// mock getters
const _getters = {
getFoo: getters.getFoo(state)
}
it('returns a collection of fancy names', () => {
expect(getters.getFancyNames(state, _getters)).toEqual([
'foo: stackoverflow',
'bar: stackoverflow'
]);
});
});
When the tested getter depends on other getter that has arguments this means that I've reference the original getter.getFoo
on the mock, and this breaks the idea of mocking, since the tests start to have relation with each other. When the getters grow, and the dependency graph has several levels it makes the tests complex.
Maybe this is the way to go, just wanted to check that I'm not missing anything...
Upvotes: 7
Views: 3415
Reputation: 3756
A cleaner way that I have found is to create your own mocked getters object. This only works if the getter
uses the unaltered state
like the question does.
const state = {
name: 'stackoverflow'
}
describe('getFancyNames', () => {
const mockedGetters = {
...getters, // This can be skipped
getFoo: getters.getFoo(state), // We only overwrite what is needed
};
it('returns a collection of fancy names', () => {
expect(getters.getFancyNames(state, mockedGetters)).toEqual([
'foo: stackoverflow',
'bar: stackoverflow'
])
})
})
In the case that you do need to call other getter functions just pass the mocked getters objects into another mocked getters object. It sounds worse than is actually is.
getters.py
export const getters = {
getBar(state) = { // new extra hard part!
return state.bar,
},
getFoo(state, getters) => prefix {
return `${prefix}: ${state.name} with some ${getters.getBar}`;
},
getFancyNames(state, getters) {
return [
getters.getFoo('foo'),
getters.getFoo('bar')
]
}
}
const _mockedGetters = {
...getters, // This can be skipped
getFoo: getters.getFoo(state), // We only overwrite what is needed
};
const mockedGetters = {
.._mockedGetters, // Use the mocked object!
getBar: getters.getBar(state, _mockedGetters), // We only overwrite what is needed
};
// continue down the line as needed!
Upvotes: 1
Reputation: 4782
Since I'm using Jest there is an option in the jest mock function that let's specify the return value when called:
mockReturnValueOnce
or mockReturnValue
More information can be found here: https://facebook.github.io/jest/docs/en/mock-functions.html#mock-return-values
Using the same code as in the question this could be solved like this:
const state = {
name: 'stackoverflow'
}
describe('getFancyNames', () => {
const getFoo = jest.fn()
getFoo.mockReturnValueOnce('foo: stackoverflow')
getFoo.mockReturnValueOnce('bar: stackoverflow')
it('returns a collection of fancy names', () => {
expect(getters.getFancyNames(state, { getFoo })).toEqual([
'foo: stackoverflow',
'bar: stackoverflow'
])
})
})
Upvotes: 2
Reputation: 280
I agree with you that referencing the actual collaborator in your mock defeats the purpose of a mock. So instead I would simply directly return whatever you want your collaborator to return.
In your example, instead of doing something like this:
// mock getters
const _getters = {
getFoo: getters.getFoo(state)
}
You would simply put in whatever getters.getFoo(state)
would return:
const _getters = {
getFoo: 'foobar'
}
If you have a getter that takes an additional argument you would simply return a function that returns a constant:
const _getters = {
getFoo: x => 'foobar',
}
Upvotes: 4