How to test router code that contains heavy logic using sinon and stubbing (nodeJS) - sinon

I am new to using sinon, so sorry if my question is weird, I looked everywhere but can't find a way to do it.
I have app with express router. I want to write uint test for one of the routes. That route have an inner function that is 'heavy', meaning that it is async with promise, and in reality calls an external api. I want to stub that inner function in the test so that it will not use the api, and will return my own data instead of the original method.
This is the code so far:
routes/setOrder.js:
// the inner function I want to stub
var verifyPayment = function(saleId) {
return new Promise((resolve, reject) => {
logger.info(`verifyPayment: ${saleId}`);
externalAPICall.get( // <==this is the 'heavey part!!
saleId,
function (error, sale) {
if(error) {
return reject(`Error querying sale(${saleId}): ${error}`);
}
resolve(sale);
});
});
}
router.get('/paymentId/:paymentId', setOrderWithGet);
const setOrderWithGet =async function(req, res, next) {
const { paymentId } = req.params;
verifyPayment(paymentId)
.then(async sale => {
try {
console.log(`sale:${sale}`);
res.send(JSON.stringify({"status": "ok!" }));
} catch (err) {
logger.warn(err)
res.send(JSON.stringify({"status": "fail.."}));
}
})
.catch(reason => {
logger.warn(`[] Payment(${paymentId}) is not valid ${reason}`);
res.send(JSON.stringify({"status": "fail.."}));
});
}
module.exports = router;
module.exports.setOrderWithGet = setOrderWithGet;
module.exports.verifyPayment = verifyPayment;
setOrderTest.js:
const setOrderStub = require('../routes/setOrder');
describe("POST /setOrder", () => {
beforeEach(() => {
sinon.stub(setOrderStub, 'verifyPayment').resolves({....});
});
afterEach(() => {
sinon.restore();
});
describe("test1", () => {
it("setOrder first attempt", () => {
let req ={params : {'paymentId' : 'mypamentid1'}};
setOrderStub.setOrderWithGet(req,{});
});
});
});

This line:
sinon.stub(setOrderStub, 'verifyPayment').resolves({....});
...stubs the verifyPayment function on the module exports of the setOrder module.
Right now setOrderWithGet is calling the verifyPayment function directly, so it is unaffected by any changes to the module exports.
Change setOrderWithGet to call verifyPayment using the module exports:
const setOrderWithGet = async function(req, res, next) {
// ...
module.exports.verifyPayment(paymentId) // <= call the module export for verifyPayment
// ...
}
...and your stub will get called.

Related

how to unsubscribe () a firebase collection in a vue.js component

In a vuejs component which dynamically retrieves data with firebase I would like to unsubscribe when I quit the component.
In the firebase documentation indicates that you must use the unsubscribe() function; to stop listening to the collection.
Unfortunately, this function cannot be used directly because it is declared undefined.
Here is the component code:
<script>
import db from "../../firebase/init";
let subscribe;
export default {
// ...
beforeDestroy() {
// Don't work form me !!!
unsubscribe();
},
methods: {
async getMyCollection() {
try {
subscribe = await db.collection("myCollection");
subscribe.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
// Do something
}
});
});
} catch (error) {
console.log(error);
}
}
}
</script>
thanks for the help
Its because you have not defined the unsubscribe anywhere. Please check the code below.
<script>
import db from "../../firebase/init";
let unsubscribe;
export default {
// ...
beforeDestroy() {
unsubscribe();
},
methods: {
async getMyCollection() {
try {
unsubscribe = await db.collection("myCollection")
.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
// Do something
}
});
});
} catch (error) {
console.log(error);
}
}
}
</script>

How to unit test gutenberg class/component method that uses wp.apiFetch

I am trying to unit test a method on a class/component that calls the wp.apiFetch method but when I run the test it always says TypeError: apiFetch is not a function the below structure has worked for other test cases, all be it they don't make any api calls.
Stripped down test setup
global.wp = {};
// some other packages
Object.defineProperty(global.wp, 'apiFetch', { get: () => require('#wordpress/api-fetch') });
Stripped down test
Shallow render the component
Call the method in question on the component instance - at this point here the tests error because of the error mentioned at the top.
Check that the method was called
it('createAuthor valid', () => {
// may have to mock window.fetch as wp.apiFetch uses it but that's currently not the issue
const componentProps = {
someFunc: jest.fn(),
};
const component = shallow(<NewAuthor {...componentProps} />);
component.setState({ name: 'john smith' });
component.instance().createAuthor();
expect(componentProps.someFunc).toHaveBeenCalledTimes(1);
});
Stripped down component
const { apiFetch } = wp;
// some more constants from window
class NewAuthor extends Component {
// constructor
// method to test
createAuthor = () => {
const { someFunc, someErrorFunc } = this.props;
const authorObject = {
name,
} = this.state;
const createPath = '/wp/v2/users';
apiFetch({
path: createPath,
data: authorObject,
method: 'POST',
})
.then((user) => {
console.log('success', user);
someFunc();
})
.catch((error) => {
console.log('error', error);
someErrorFunc();
});
};
// some more methods
// render
}
export default NewAuthor;

How to execute this prop as a function in the if/else statement?

I want to change the state in the redux reducer if GoogleMap DirectionService returns an error.
How to use redux-thunk logic in the redux actions file if I use react-google-maps package and the app receiving data inside the component file that uses this package?
componentDidMount() {
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route({
//some state
}, (result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: {...result},
markers: true
})
} else {
this.props.HOW_TO_EXECUTE_THIS_PROP?;
}
});
}
const mapDispatchToProps = (dispatch) => {
return {
HOW_TO_EXECUTE_THIS_PROP?: () => dispatch(actions.someAction()),
}
}
Generally, you will be able to simply call the prop method you're passing in. So if your code reads:
const mapDispatchToProps = (dispatch) => {
return {
propToExecute: () => dispatch(actions.someAction()),
}
}
... then you will call it inside your componentDidMount as:
this.props.propToExecute();
However, since we're using ES6, let's format it correctly, please:
const mapDispatchToProps = dispatch => ({
propToExecute: () => dispatch(actions.someAction())
})

dispatching inside actions in action creator

Can we use dispatch inside action creators and what purpose do they serve inside action creators ?
Here is a sample modified code from codebase .
export default function xyz(data) {
const url = ;
return function (dispatch) {
dispatch(
a()
);
callApi(url, REQUESTS.POST, HEADERS, data).then((response) =>{
dispatch(
b(data)
);
}).catch((error) => {
dispatch(
c(error.toString())
);
});
};
}
// this returns a type (an object)
export function a() {
return {
type: xyzzzz
};
}
Similarly we have b and c returning either type or say objects .
Yes, you can dispatch multiple actions in an action.
I usually put a dispatch on an asychnronous action like this
function action() => {
return async (dispatch) => {
let payload;
dispatch('before-request');
try {
await someAsyncProcess();
payload = { status: 'success' };
} catch (err) {
payload = { status: 'failure' };
}
dispatch('after-request', payload);
};
}

Redux Chain Multiple Ajax Promises in one Action

I have a Redux action which needs to make 2 subsequent ajax calls.
The first calls googlemaps api: https://maps.googleapis.com/maps/api/geocode/json?address=${searchTerm}&key=${gmapsKey}
The second calls a local service based on those results
/api/content/stores/byDistance/${lat},${lng}/sort
I'm using superagent to do the ajax calls. Clearly I'm experience difficulties keeping track of the promises, and including failures.
Am I mis-undestanding a core concept of Promises? Is there a simpler way to write the below?
export function loadBySearch(searchTerm) {
const geoSearchUrl = `https://maps.googleapis.com/maps/api/geocode/json?address=${searchTerm}&key=${gmapsKey}`;
return {
types: [LOAD, LOAD_BY_LAT_LONG, LOAD_FAIL],
//Do I need to make this promise here?
promise: (client) => {
const promise = new Promise( (resolve, reject) => {
console.info('making google geocode request', geoSearchUrl);
superagent.get(geoSearchUrl)
.set('Accept', 'application/json')
.then( (successData1) =>{
const results = successData1.body.results;
if (!results.length) {
reject(`no results found for this search : ${searchTerm}`);
return;
}
const lat = results[0].geometry.location.lat;
const lng = results[0].geometry.location.lng;
const path = `/api/content/stores/byDistance/${lat},${lng}/sort`;
client.get(path).then(
(successData2) => {
resolve( {
searchTerm: searchTerm,
searchLocation: {
lat,
lng
},
data: successData2
});
},
(errorData2) => {
reject( {
searchTerm: searchTerm,
result: errorData2
});
},
);
},
(errorData1) => {
reject({
searchTerm: searchTerm,
result: errorData1
});
}
);
});
return promise;
}
};
}
I'm not using superagent, but I'm guessing something like this might just work:
superagent.get(geoSearchUrl)
.set('Accept', 'application/json')
.then(successData1 => {
const results = successData1.body.results;
if (!results.length) {
throw(`no results found for this search : ${searchTerm}`);
}
return Promise.resolve(results);
})
.then(results => {
const lat = results[0].geometry.location.lat;
const lng = results[0].geometry.location.lng;
const path = `/api/content/stores/byDistance/${lat},${lng}/sort`;
return client.get(path);
})
.then(successData2 => {
return Promise.resolve({
searchTerm: searchTerm,
searchLocation: {
lat,
lng
},
data: successData2
});
})
.catch(error => {
return Promise.reject({
searchTerm: searchTerm,
result: error
});
});
Haven't test it, but I hope at least it helps ;)

Resources