How to run concurrent tests in Jest with multiple tests per request? - jestjs

I'd like to run my Jest tests concurrently, but I'm having issues with one scenario:
I'm testing the results on an endpoint, and I want to test multiple things about it. So in my beforeAll function, I make the request and store the response, and then I test the response in multiple tests. This works fine synchronously, but when I make the tests concurrent, it no longer lets you pass a variable into the test, so it's a no go. Alternatively, I can put the request in the test itself and then expect many things about the response, but then I don't have the granularity to see what went wrong if something fails.
Is there any solution for this scenario?
This works:
let data;
beforeAll(async () => {
data = await getDataFromRequest();
}
it('value1 should be truthy', () => {
expect(data.value1).toBeTruthy();
}
it('value2 should be truthy', () => {
expect(data.value2).toBeTruthy();
}
This also works:
it.concurrent('data should have correct values', async () => {
const data = await getDataFromRequest();
expect(data.value1).toBeTruthy();
expect(data.value2).toBeTruthy();
}
But what I want is:
let data;
beforeAll(async () => {
data = await getDataFromRequest();
}
it.concurrent('value1 should be truthy', () => {
expect(data.value1).toBeTruthy();
}
it.concurrent('value2 should be truthy', () => {
expect(data.value2).toBeTruthy();
}

Seems worth pointing out, that there is also a discussion about this in a jest issue: https://github.com/facebook/jest/issues/4281
Gist of it: It doesn't work that way and isn't planned. Possible workaround:
const dataPromise = getSomeDataPromise();
test.concurrent('one', async () => {
const data = await dataPromise;
});
test.concurrent('two', async () => {
const data = await dataPromise;
});

Was having same issue when doing browser testing with Playwright where one test suite only requires one instance of browser. Had to wrap it with a Promise with setInterval. In your case it should be like below:
let data;
beforeAll(async () => {
data = await getDataFromRequest();
}
test.concurrent('value1 should be truthy', async () => {
await waitForData();
expect(data.value1).toBeTruthy();
}
test.concurrent('value2 should be truthy', async () => {
await waitForData();
expect(data.value2).toBeTruthy();
}
/**
* #param {number} interval - the interval to check data availability
* #param {number} timeout - the overall timeout value
* #return Promise of your data OR reject if timeout.
*/
function waitForData(interval = 500, timeout = 5000){
let acc = 0; // time accumulation
return new Promise((resolve, reject) => {
const i = setInterval(() => {
acc += interval;
if (data) {
clearInterval(i);
resolve(data);
}
if (acc > timeout) {
clearInterval(i);
reject();
}
}, interval);
});
}
So you just need to assign the proper check interval and timeout which should be long enough for your data asycn call to come back.

Related

manage stored variables in Chrome extendion

I am trying to get the hang of managing persistent variables to be used across my first chrome extension. If I get it correctly the proper way is to use storage (i am using storage.local in my case).
In my background.js script I set up the code as below, with 3 functions to initialize a variable (to 0), update it (by increasing it by 1 everytime the updateApiCalls() function is called) and to print it console to check if the process worked:
const resetApiCalls = () => {
chrome.storage.local.set({"apiCalls": 0})
};
const updateApiCalls = () => {
chrome.storage.local.get(["apiCalls"]).then((items) => {
chrome.storage.local.set({"apiCalls": items.apiCalls + 1})
});
};
const printApiCalls = () => {
chrome.storage.local.get(["apiCalls"]).then((items) => {
console.log("apicalls: " + items.apiCalls);
});
};
resetApiCalls();
updateApiCalls();
printApiCalls();
The result is that the printApicalls() still logs 0 to console as if the variable didn't get changed by updateApiCalls().
What I am doing wrong in my code?
Or am I completely off track and should use a completely different approach to perform this task?
const resetApiCalls = () => {
return chrome.storage.local.set({"apiCalls": 0})
};
const updateApiCalls = async () => {
var items = await chrome.storage.local.get(["apiCalls"]);
return chrome.storage.local.set({"apiCalls": items.apiCalls + 1})
};
const printApiCalls = () => {
chrome.storage.local.get(["apiCalls"]).then((items) => {
console.log("apicalls: " + items.apiCalls);
});
};
(async _ => {
await resetApiCalls();
await updateApiCalls();
printApiCalls();
})()
/*
//you can also write this way
resetApiCalls().then(updateApiCalls).then(printApiCalls);
*/
The 3 functions are asynchronous so you have to find a way to execute them one after the other synchronously otherwise the result is unpredictable.
I transformed the first 2 functions so that they returned a promise after which I waited for each of these promises to be fullfilled to execute the next function.
On the surface you're doing everything correctly.
However there is a caveat with chrome storage... quote from their api.
Storage and throttling limits
Don't think of adding to the Storage API as putting things in a big truck. Think of adding to storage as being like putting something in a pipe. The pipe may have material in it already, and it may even be filled. Always assume a delay between when you add to storage and when it is actually recorded.
Two examples with working results.
Using async/await
const resetApiCalls = async () => {
await chrome.storage.local.set({"apiCalls": 0})
};
const updateApiCalls = async () => {
const result = await chrome.storage.local.get(["apiCalls"]);
const incremented = result.apiCalls + 1;
await chrome.storage.local.set({"apiCalls": incremented})
};
const printApiCalls = async () => {
const result = await chrome.storage.local.get(["apiCalls"]);
console.log("result: ", result);
};
(async function () {
await resetApiCalls();
await updateApiCalls();
await printApiCalls(); //console.log prints {"apiCalls": 1}
})();
using a setTimeout as a test with your code.
const resetApiCalls = () => {
chrome.storage.local.set({"apiCalls": 0})
};
const updateApiCalls = () => {
chrome.storage.local.get(["apiCalls"]).then((items) => {
chrome.storage.local.set({"apiCalls": items.apiCalls + 1})
});
};
const printApiCalls = () => {
chrome.storage.local.get(["apiCalls"]).then((items) => {
console.log("apicalls: " + items.apiCalls);
});
};
resetApiCalls();
updateApiCalls();
// printApiCalls();
setTimeout(function() {
printApiCalls(); //console.log prints "apicalls: 1"
}, 5000);
Hope this clears up the issue.

How to use wrapper with beforeEach and afterEach?

Every test I made with jest it's begining with await app.transaction(async(trx) => { and ends with ..rollback..
await app.transaction(async(trx) => {
const a = await update();
expect(a).toBe(something);
await trx.rollback();
});
The actual test is:
const a = await update();
expect(a).toBe(something);
And I want instead of write this wrapper for every test function, just to write within the beforeEach and afterEach.
Since the test is inside of the parameter to transaction you can't really do this in a beforeEach since it will differ based on the test. However you can avoid duplicating the code in each test by writing a helper function like this:
async function wrapper(testFn) {
return app.transaction(async(trx) => {
await testFn();
return trx.rollback();
}
}
// then do this in each test:
it('should work', () => {
await wrapper(async () => {
const a = await update();
expect(a).toBe(something);
});
});

Put time limit on Jest test?

I'm running a set of tests with Jest to demonstrate Big O using two different methods for Fibonacci.
const fastFib = require('./../fastFib');
const slowFib = require('./../slowFib');
test('Fast way of getting Fibonacci of 44', () => {
expect(fastFib(44)).toBe(701408733);
});
test('Slow way of getting Fibonacci of 44', () => {
expect(slowFib(44)).toBe(701408733);
});
I'm wondering if there is a way to specify the maximum length of a test? I saw you can pass a third variable for an async timeout but it doesn't seem to have any effect on normal functions:
test('Slow way of getting Fibonacci of 44', () => {
expect(slowFib(44)).toBe(701408733);
}, 5000);
Is there a way I can specify the maximum execution time for a function with Jest?
I will share slowFib.js for reference:
function fib(n) {
return (n<=1) ? n : fib(n - 1) + fib(n - 2);
}
module.exports = fib;
so your test pauses because of sync execution - there is no way to interrupt that by timeout. You need to "split execution". Next version fails to me:
test('Slow way of getting Fibonacci of 44', (done) => {
expect(slowFib(44)).toBe(701408733);
setTimeout(done, 10); // smallest timeout possible to make different macrotask
}, 5000);
PS I also believe this should be achievable by marking test async but have not yet figured how exactly.
[UPD] you actually may achieve your goal without using test's timeout:
test('Slow way of getting Fibonacci of 44', () => {
const start = new Date();
expect(slowFib(44)).toBe(701408733);
expect(new Date() - start).toBeLessThan(5000);
});
In your test file you can set
jest.setTimeout(5000); which overrides the default timeout for a test case in jest
I think you would need to implement your own timer (with setTimeout or using Promises). One alternative would be to use the async keyword for your function to make it work with the built-in parameter :
test('Slow way of getting Fibonacci of 44', async () => {
expect(slowFib(44)).toBe(701408733);
}, 5000);
Here's a (typescript friendly) functions inspired by #skyboyer's suggestion.
(tested using Jest 24.8.0, but should be relevant to any version)
// Takes a threshold and a callback.
// if the callback didn't run within the threshold, the returned function will evaluate to a rejected promise.
// else, the returned function will evaluate to a resolved promise with the value returned by 'cb' (T)
const resolveWithin = <T>(threshold: number, cb: () => T): () => Promise<T> => {
return () => {
const start = Date.now();
return Promise.resolve(cb()).then((t: T) => {
const elapsed = Date.now() - start;
if (elapsed > threshold) {
return Promise.reject(elapsed);
}
return Promise.resolve(t);
});
}
};
// Uses "resolveWithin" to ensure a test has run within the threshold.
const withIn = <T>(threshold: number, fn: () => T): () => Promise<T> => {
const cb = resolveWithin(threshold, fn);
// #ts-ignore
return () => {
return cb().catch((elapsed) => {
expect(elapsed).toBeLessThanOrEqual(threshold);
})
}
};
it("example", withIn(1000, () => { ... }));
it("example", withIn(1000, async () => { ... }));
A note regarding #Khaled Osman's / #HRK44 answers.
From what I could tell, using either approach will not be reflected as a test failure, and will not appear in reports generated by Jest.

TypeError: firestoreService.snapshot_ is not a function

I've been using firebase functions test to do some testing on my functions. I have some code that is supposed to post a thing to firestore, basically in the same way that the examples show to do in the realtime database examples:
exports.addMessage = functions.https.onRequest((req, res) => {
const original = req.query.text;
admin.firestore()
.collection('messages')
.add({ original })
.then(documentReference => res.send(documentReference))
.catch(error => res.send(error));
});
For my test, I've spoofed some basic functionality using sinon, mocha and chai. Here is my current test, which is failing with the error message: TypeError: firestoreService.snapshot_ is not a function
describe('addMessage', () => {
// add message should add a message to the database
let oldDatabase;
before(() => {
// Save the old database method so it can be restored after the test.
oldDatabase = admin.firestore;
});
after(() => {
// Restoring admin.database() to the original method.
admin.firestore = oldDatabase;
});
it('should return the correct data', (done) => {
// create stubs
const refStub = sinon.stub();
// create a fake request object
const req = {
query : {
text: 'fly you fools!'
}
};
const snap = test.firestore.makeDocumentSnapshot({ original: req.query.text }, 'messages/1234');
// create a fake document reference
const fakeDocRef = snap._ref;
// create a fake response object
const res = {
send: returnedDocRef => {
// test the result
assert.equal(returnedDocRef, fakeDocRef);
done();
}
};
// spoof firestore
const adminStub = sinon.stub(admin, 'firestore').get(() => () => {
return {
collection: () => {
return {
add: (data) => {
const secondSnap = test.firestore.makeDocumentSnapshot(data, 'messages/1234');
const anotherFakeDocRef = secondSnap._ref;
return Promise.resolve(anotherFakeDocRef);
}
}
}
}
});
// call the function to execute the test above
myFunctions.addMessage(req, res);
});
});
My question is how the heck do I fix this?
I previously had a test that was just passing the first snap and fakeDocRef, and my test was passing fine, but as soon as I resolve the promise with the new fake document reference, it fails...
Any help would be appreciated! Thanks!
There are three different types of the calls, that are different:
Operating on the Collections.
Operating on the Documents.
Operating on the results of the query.
They have to be used consistently.
Please refer a documentation to see the difference operation on the collection and the document.

How do I test this async method call in reactjs using mocha

// Balance.jsx
...
updateToken () {
const parseResponse = (response) => {
if (response.ok) {
return response.json()
} else {
throw new Error('Could not retrieve access token.')
}
}
const update = (data) => {
if (data.token) {
this.data.accessTokenData = data
} else {
throw new Error('Invalid response from token api')
}
}
if (this.props.balanceEndpoint !== null) {
return fetch(this.props.accessTokenEndpoint, {
method: 'get',
credentials: 'include'
})
.then(parseResponse)
.then(update)
.catch((err) => Promise.reject(err))
}
}
componentDidMount () {
this.updateToken()
.then(() => this.updateBalance())
}
}
// Test
it('updates the balance', () => {
subject = mount(<Balance {...props} />)
expect(fetchMock.called('balance.json')).to.be.true
})
I can't figure out how to test the above using Mocha. The code is does work the method updateBalance is called and the fetch api call actually does happen, but the test still fails. If I call updateBalance() synchronously it passes... How do I tell the test to wait for the promise to resolve?
You don't really say what you want to test that the
method does, but if all you want to test is that the method resolves on a network call, then there is no need for Sinon or any of that, as this is all you need:
describe("BalanceComponent", () => {
it("should resolve the promise on a successful network call", () => {
const component = new BalanceComponent({any: 'props', foo: 'bar'});
// assumes you call a network service that returns a
// successful response of course ...
return component.updateToken();
});
});
This will test that the method actually works, but it is slow and is not a true unit test, as it relies on the network being there and that you run the tests in a browser that can supply you with a working implementation of fetch. It will fail as soon as you run it in Node or if the service is down.
If you want to test that the method actually does something specific, then you would need to to that in a function passed to then in your test:
it("should change the token on a successful network call", () => {
const component = new BalanceComponent({any: 'props', foo: 'bar'});
const oldToken = component.data.accessTokenData;
return component.updateToken().then( ()=> {
assert(oldToken !== component.data.accessTokenData);
});
});
If you want to learn how to test code like this without being reliant on there being a functioning link to the networked service you are calling, you can check out the three different techniques described in this answer.

Resources