I'm trying to generate tests dynamically by looping over an array returned from an async call. I just cannot figure out how to do this - either using mocha or using jest. To illustrate using code, the following synchronous example works:
describe("Test using various frameworks", () => {
["mocha", "jest"].forEach(framework => {
it(`Should test using ${framework}`, () => {
expect(true).toBe(true);
});
});
});
However, if that array is fetched asynchronously, I cannot get the testing frameworks to wait until the array is fetched before trying to loop over it.
async function getFrameworks() {
//TODO: get it from some async source here
return ["mocha", "jest"];
}
describe("Test using various frameworks", () => {
var frameworks;
//before() instead of beforeAll() if using mocha
beforeAll(async ()=> {
frameworks = await getFrameworks();
});
frameworks.forEach(framework => {
it(`Should test using ${framework}`, () => {
expect(true).toBe(true);
});
});
});
This fails saying Cannot read property 'forEach' of undefined. I've tried all sort of combinations of using async/await and Promise and passing in a done callback but to no avail.
The closest I came to this was using Mocha's --delay flag, but that only solves part of the problem. What I really want to do in my actual use case is to run some async intialization in the before() or beforeAll() hooks which I then use to dynamically generate tests.
Any pointers on how to do this using either mocha or jest?
To answer my own question, I did not find a way to do this using Jest or Mocha, but I could accomplish it using tap - which I used through babel-tap.
import tap from "babel-tap";
async function getFrameworks() {
//TODO: get it from some async source here
return ["mocha", "jest"];
}
getFrameworks().then(frameworks => {
frameworks.forEach(framework => {
tap.test(`Should test using ${framework}`, (tester) => {
tester.ok("It works!");
});
});
});
You can do a lot more though. You can create nested scopes by further calling tester.test() for example. Also, since tap does not have the concept of before, after etc, (unless you use the Mocha-like DSL ), you can simply use imperative code to simulate the equivalent behavior.
Also, you can freely use async/await style calls inside tests.
Mocha has some support async tests. Specifically, your it() callback can be async. Here is how to rearrange your tests to "looping over an array returned from an async call":
const chai = require('chai');
const { expect } = require('chai');
function getMyTestData() {
return new Promise(resolve => setTimeout(() => resolve(['mocha', 'jest']), 1000));
}
describe('Test using various frameworks', () => {
it('Should test by looping over an array returned from an async call', async () => {
const myTestData = await getMyTestData();
for(let datum of myTestData) {
expect(datum.length).greaterThan(4, `Data '${datum}' not long enough`);
}
})
});
Related
When trying to test the component which dispatches async thunk I get the following warnings. They are displayed because of updates performed after the test is finished.
console.error
Warning: An update to App inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
at App (/home/karlosos/Dev/nokia/playground/testing-redux/src/App.tsx:6:34)
at Provider (/home/karlosos/Dev/nokia/playground/testing-redux/node_modules/react-redux/lib/components/Provider.js:19:3)
at Wrapper (/home/karlosos/Dev/nokia/playground/testing-redux/src/testUtils.tsx:11:22)
at printWarning (node_modules/react-dom/cjs/react-dom.development.js:86:30)
at error (node_modules/react-dom/cjs/react-dom.development.js:60:7)
at warnIfUpdatesNotWrappedWithActDEV (node_modules/react-dom/cjs/react-dom.development.js:27589:9)
at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:25508:5)
at forceStoreRerender (node_modules/react-dom/cjs/react-dom.development.js:16977:5)
at Object.handleStoreChange [as callback] (node_modules/react-dom/cjs/react-dom.development.js:16953:7)
at node_modules/react-redux/lib/utils/Subscription.js:23:20
This is explanation why warnings are visible.
One way of fixing them is to introduce some kind of barrier that will wait for all pending async actions to be finished. But doing that my tests would need to have asserts for logic that I don't want to test.
I have recreated a minimal reproducible project here: https://github.com/karlosos/react-redux-async-warnings/tree/main/src
My example test looks like this:
test('WHEN component rendered THEN counter value is being loaded', () => {
// WHEN
renderWithProviders(<App />)
// THEN
expect(Api.getValue).toHaveBeenCalledTimes(1);
const loadingSpinner = screen.getByTestId('loading-spinner');
expect(loadingSpinner).toBeInTheDocument();
// things will happen to the component here after test is done
// precisely the data fetched from API will be displayed
});
and example thunk:
export const fetchCounterValue = (): AppThunk => async (dispatch, getState) => {
if (getState().counter.fetchValueStatus === "loading") {
return;
}
dispatch(fetchValueStart());
try {
const result = await Api.getValue();
dispatch(fetchValueSuccess(result));
} catch (e) {
dispatch(fetchValueError('Could not fetch the data'));
}
};
Update 1 - No warnings when using waitFor at the end of the test
When I've added await waitFor(() => new Promise(res => setTimeout(res, 0))); at the end of the test then warnings are not visible. But I don't want to edit every single test case. It seems like a hack.
test('WHEN component rendered THEN counter value is being loaded', async () => {
// WHEN
renderWithProviders(<App />)
// THEN
expect(Api.getValue).toHaveBeenCalledTimes(1);
const loadingSpinner = screen.getByTestId('loading-spinner');
expect(loadingSpinner).toBeInTheDocument();
await waitFor(() => new Promise(res => setTimeout(res, 0)));
});
When you first update your tests to use createRoot, you may see this
warning in your test console:
The current testing environment is not configured to support act(…)
https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#configuring-your-testing-environment
So i'm currently trying to pull data inside Nuxt from Cloud Firestore via:
async asyncData(){
return firebase.firestore().collection('test').doc().get()
.then((result) => {
result.forEach(doc => {
console.log(doc.data())
})
})
}
which results in the following error:
async asyncData() {
return _plugins_firebase_config__WEBPACK_IMPORTED_MODULE_0__["default"].firestore().collection('test').doc().get().then(result => {
result.forEach(doc => {
console.log(doc.data());
});
});
}
with the error message:
TypeError
result.forEach is not a function
Pulling data without forEach works fine. So if i specify the doc by the name and get data only for one document, everything works as expected.
However, since i need the whole collection of the data, i have to split up the data via forEach. Any ideas what could cause that?
Same example works in VueJS itself just fine.
I've used nuxt cli to generate a new project, so the project setup should be fine.
First get and modify Your data, then return it.
Modify Your code so it looks like this:
async asyncData () {
let data = []
await firebase.firestore()
.collection('test')
.doc()
.get()
.then((result) => {
result.forEach(doc => {
data.push(doc.data())
})
})
return data
}
I detected some recursion on one of the nodes of my realtime database and I want to delete (or set tu null) that specific node. This is my firebase function so far:
exports.cleanForms = functions.https.onRequest((req, res) => {
const parentRef = admin.database().ref("forms");
return parentRef.once('value').then(snapshot => {
snapshot.forEach(function(child) {
admin.database().ref('forms/'+child.key+'/user/forms').set(null);
});
});
});
Basically it should iterate all the records inside the forms node and delete its user/forms property.
But calling that function by going to this url: https://.cloudfunctions.net/cleanForms gives me this error:
Error: could not handle the request
And this is what I see on the logs:
10:47:57.818 PM cleanForms Function execution took 13602 ms, finished
with status: 'connection error'
The forms node has less than 3,000 records but as I mentioned before, it has some recursion on it. I don't know if it is failing due to its size or something related to that.
You are using an HTTPs Cloud Function: therefore you must "send a response to the client at the end" (Watch this official video by Doug Stevenson for more detail: https://youtu.be/7IkUgCLr5oA).
In your case, the "end" of the function will be when ALL of your set() asynchronous operations will be "done". Since the set() method returns a Promise, you have to use Promise.all() (again, watch this official video: https://youtu.be/d9GrysWH1Lc ).
So the following should work (not tested however):
exports.cleanForms = functions.https.onRequest((req, res) => {
const parentRef = admin.database().ref("forms");
parentRef.once('value')
.then(snapshot => {
const promises = [];
snapshot.forEach(child => {
promises.push(admin.database().ref('forms/'+child.key+'/user/forms').set(null));
});
return Promise.all(promises)
.then(results => {
response.send({result: results.length + ' node(s) deleted'});
})
.catch(error => {
response.status(500).send(error);
});
});
New to Jest and Redux and I'm having trouble with testing functions that are dispatching to the store but don't yield a return value. I'm trying to follow the example from the Redux website does this
return store.dispatch(actions.fetchTodos()).then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions)
})
however I have several "fetchtodos" functions that don't return anything which causes the error TypeError:
Cannot read property 'then' of undefined due to returning undefined
I'm wondering what I can do to test that my mock store is correctly updating. Is there a way to dispatch the function, wait for it to finish and then compare the mock store with expected results?
Thanks
Edit: We're using typescript
action from tsx
export function selectTopic(topic: Topic | undefined): (dispatch: Redux.Dispatch<TopicState>) => void {
return (dispatch: Redux.Dispatch<TopicState>): void => {
dispatch({
type: SELECT_Topic,
payload: topic,
});
dispatch(reset(topic));
};
}
test.tsx
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('Select Topic action', () => {
it('should create an action to select .', () => {
const topic: Topic = mockdata.example[0];
const expectedAction = {
type: actions.SELECT_TOPIC,
payload: topic,
};
const store = mockStore(mockdata.defaultState);
return store.dispatch(actions.selectTopic(topic)).then(() => {
expect(store.getState()).toEqual(expectedAction);
});
});
});
The action is what I'm given to test(and there are many other functions similar to it. I'm getting that undefined error when running the test code, as the function isn't returning anything.
In Redux, the store's dispatch method is synchronous unless you attach middleware that changes that behavior, ie: returns a promise.
So this is likely a redux configuration problem. Be sure you are setting up your test store with the same middleware that allows you to use the promise pattern in production.
And as always, be sure to mock any network requests to avoid making api calls in test.
Trying to use this.subscribe('users') of latest meteor, I would like to know as how to attach function or have callback function along with this
Previously we could have a call back function using then, how can we achieve this in latest angular meteor?
any kind of help or links are appreciated.
this.subscribe('users', () => {
return [foo, bar]; // publication arguments
}, {
onReady: (res) => {
// success callback
},
onError: (err) => {
// error callback
}
});