I have a Vue button click handler that, depending on the arguments it receives, can do the following:
- execute request A only
- execute request B only
- execute request A and then request B sequentially (request B is only called if request A completes successfully. It's important to note that the implementation cannot use Promise.all()).
My issue is figuring out how to write unit tests in Jest for the "execute A and then B sequentially" behavior.
Code Implementation
Below is the event handler that runs after clicking a button:
const loadA = this.$store.dispatch['loadA']; //these are FUNCTIONS THAT RETURN PROMISES
const loadB = this.$store.dispatch['loadB'];
async makeRequests(shouldMakeRequestA, shouldMakeRequestB) {
const requests = [];
if(shouldMakeRequestA) requests.push(loadA);
if(shouldMakeRequestB) requests.push(loadB);
for(const request of requests) {
await request(); //waits for request to complete before proceeding to the next one
}
}
Testing Approach
The ideal test case should:
FAIL❌ when:
the implementation calls both requests concurrently like:
() => { Promise.all([loadA(), loadB()]) }
() => { loadA(); loadB() }
PASS✔️ when:
- the implementation executes loadA, waits for its promise to resolve, then executes loadB, e.g.:
() => {await loadA(); await loadB();}
- the implementation executes loadA, waits for its promise to resolve, then executes loadB, e.g.:
Below is my attempt at writing the test case, but I acknowledge it might be vulnerable to race conditions and not easily understood by colleagues.
//component.spec.js
import MyCustomButton from '@/components/MyCustomButton.vue'
import TheComponentWeAreTesting from '@/components/TheComponentWeAreTesting'
describe('foo', () => {
const resolveAfterOneSecond = () => new Promise(resolve => setTimeout(resolve, 1000));
let wrapper;
const loadA = jest.fn(resolveAfterOneSecond);
const loadB = jest.fn(resolveAfterOneSecond);
beforeEach(() => {
wrapper = shallowMount(TheComponentWeAreTesting, store: new Vuex.Store({actions: {loadA, loadB}});
})
it('executes A and B sequentially', async () => {
wrapper.find(MyCustomButton).vm.$emit('click');
await wrapper.vm.$nextTick();
await new Promise(resolve => setTimeout(resolve, 500));
const callCount = loadA.mock.calls.length + loadB.mock.calls.length;
expect(callCount).toBe(1);
}
}
Is there an alternative approach to testing this behavior? I am aware of methods like jest.advanceTimersByTime
, but it affects all timers, not just the current one.