How do I test this async method call in reactjs using mocha - node.js

// 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.

Related

Vue component doing async hangs at await while jest testing

I'm trying to test a component that loads data asynchronously when mounted. The component works as expected, it's just the test that's giving me issues. The component's async loadData() function hangs at await axios.get() while jest test runner is in the component.vm.$nextTick(). As a result, the checks in the $nextTick loop never pass.
Immediately after the $nextTick loop times out, the component's await statement completes and the component renders itself. axios is mocked, so it should resolve really fast. If I remove the await and just fill in a constant instead, the entire thing executes as expected.
I'm guessing that $nextTick loop is not asynchronous and it's consuming the thread, even though this is the recommended way of testing asynchronous stuff. The problem is, I don't have an onclick async handler to await: this method is called from onMount.
Unfortunately, I don't know how to make a jsFiddle of this one, so I hope this will be enough:
my component (the relevant parts)
export default {
data() { return { content: '' }; },
mounted() { this.loadDoc() }
methods: {
async loadDoc() {
const res = await axios.get('some url'); // <-- this is the line that hangs until timeout
// const res = { data: 'test data'}; // this would test just fine
this.content = res.data;
}
}
}
and my component.spec.js:
jest.mock('axios', () => ({
get: async (url) => {
return { data: 'test data' };
}
};
describe('my super test', () => {
it('renders', (done) => {
const doc = shallowMount(myComponent);
doc.vm.$nextTick(() => {
expect(doc.html()).toContain('test data'); // <-- this never matches
done();
});
});
});
I would delete, but I just spent quite some hours for something that was suggested in the docs, but not explained that it's the only way... I'm hoping somebody else finds this useful.
Using flush-promises package instead of $nextTick loop immediately "fixed" the problem
Code sample (rework of above):
describe('my super test', () => {
it('renders', async() => {
const doc = shallowMount(myComponent);
await flushPromises();
expect(doc.html()).toContain('test data'); // <-- now it works
});
});

Test cases where Axios errors need to be handled differently, using Jest

Assuming that I have the following function, in which I want to handle different axios erroneous responses in different ways.
async fetchData() {
const data = {...};
const response = await axios.post('endpoint_here', data)
.catch((error) => {
if (error.code === SomeKindOfError) {
throw new SomeKindOfError(error.message);
}
if (error.code === AnotherKindOfError) {
throw new AnotherKindOfError(error.message);
}
throw new Error(error.message);
});
return response.data;
}
How can I test all these different cases in my test using Jest ?
At the moment my test is this:
test('test SomeKindOfError', async () => {
axios.post.mockRejectedValueOnce(new SomeKindOfError('There was an error.'));
await expect(fetchData())
.rejects
.toThrowError(new SomeKindOfError('There was an error.'));
});
The above test is a pass, but it is nothing close to what a proper test for this function should be.
I would want to be able to check what the error returned from Axios is, and accordingly test that my own custom made errors get thrown.
Ideally, a way of mocking the Axios error response that will allow me to cover all the different cases in my tests, something like the following:
test('test SomeKindOfError', async () => {
axios.post.mockImplementation(() => Promise.reject({
error: { code: SomeKindOfError.code },
}));
await expect(fetchData())
.rejects
.toThrowError(new SomeKindOfError('There was an error.'));
});
Eventually, the solution was as simple as this:
test('test SomeKindOfError', async () => {
axios.post.mockRejectedValueOnce({ code: SomeKindOfError });
await expect(fetchData())
.rejects
.toThrowError(new SomeKindOfError('There was an error.'));
});

Nest JS - Issue writing Jest Test Case for a function returning Observable Axios Response

I am fairly new to NestJS + Typescript + RxJs tech stack. I am trying to write a unit test case using Jest for one of my functions but not sure if doing it correctly.
component.service.ts
public fetchComponents(queryParams) {
const url = this.prepareUrl(queryParams);
const data$ = this.httpService.get(url);
return data$
.pipe(map(({ data }) => data));
}
component.sevice.spec.ts
Test case works and passes
describe('fetchComponents', () => {
const query = {
limit: 10,
offset: 0
};
const result: AxiosResponse = {
data: 'Components',
status: 200,
statusText: 'OK',
headers: {},
config: {}
};
it('should return Dummy Data when called successfully', () => {
componentService.prepareUrl = jest.fn();
jest.spyOn(httpService, 'get').mockImplementation(() => of(result));
componentService.fetchComponents(market, query)
.subscribe(
(res) => {
expect(res).toEqual('Components');
}
);
});
});
Can you please provide suggestions and pointers on how exactly I should test this function. Also without using Library like marbel-rx
I am not sure if I am testing it correctly. Is there something else also which I should test?
Since Observables are asynchronous, you have to add the asynchronous done paramter and call done() after the expect that is executed last. Otherwise, jest will finish the test run after subscribe() is called without waiting for the execution of the asynchronous execution of subscribe's callback. Try to make your test fail by for example by expecting 'Komponents'. The test will not fail.
Also, I'd recommend to use mockImplementationOnce instead of mockImplementation when possible, to avoid implicitly reusing mock behaviors in later calls and therewith creating implicit dependencies.
it('should return Dummy Data when called successfully', done => {
// Add done parameter ^^^^
componentService.prepareUrl = jest.fn();
jest.spyOn(httpService, 'get').mockImplementationOnce(() => of(result));
// Prefer mockImplementationOnce ^^^^
componentService.fetchComponents(market, query)
.subscribe(
(res) => {
expect(res).toEqual('Components');
done();
// ^^^^^^ Call done() when test is finished
}
);
});

Is it possible to use the Node testing library Rewire to mock two calls to the same function so they return different results?

I'm new to mocking in Node. I'm using the library Rewire which seems to be one of the better options. I've run into a issue where I need to mock the same function twice within a method so that they return different results:
const tourId: string = await redis.read(accessToken, false);
if (tourId === null) {
logger.warn('invalid token', { accessToken });
throw boom.notFound(codes.RECORD_NOT_FOUND);
}
logger.warn('found an access token', { tourId });
const tourResponse: string = await redis.read(tourId, false);
if (tourResponse === null) {
logger.warn('tour not found', { accessToken, tourId });
throw boom.notFound(codes.RECORD_NOT_FOUND);
}
In the above example, I call the function redis.read() twice. In order to properly get the test coverage I want I would need to mock those two calls differently which I'm struggling with. Here is a test case I have so far:
it('returns a 404 error if access token not found in redis', async () => {
service.__set__({
redis: {
read: (accessToken: string): string | null => null,
},
});
service.getHandler('12345').catch((result: boom) => {
expect(result.isBoom, 'should be boom error').to.be.true;
expect(result.output.payload.statusCode, 'should be 404 error').to.equal(404);
expect(result.output.payload.message, 'should be RECORD_NOT_FOUND error').to.equal(codes.RECORD_NOT_FOUND);
});
});
Any tips are appreciated!
Answering my own question here: sinon.stub() has an onCall() method you can use to change the output for the :nth call.
https://sinonjs.org/releases/latest/stubs/#stuboncalln-added-in-v18
it('returns an iTour instance if found in redis', async () => {
const callback = sinon.stub();
callback.onCall(0).returns('12345');
callback.onCall(1).returns('67890');
service.__set__({
redis: {
read: () => callback(),
}
});
service.getHandler('12345').catch((result: boom) => {
expect(result.isBoom, 'should be boom error').to.be.true;
expect(result.output.payload.statusCode, 'should be 404 error').to.equal(404);
expect(result.output.payload.message, 'should be RECORD_NOT_FOUND error').to.equal(codes.RECORD_NOT_FOUND);
});
});

How am I suppose to stub a function which is dependent on result of previous function?

I have recently started writing tests and I don't have much experience.If any of the community member could point me in the right direction I would be really thankful. My scenario is simple I am half way through it but unable to solve my exact problem. Below is my code..
return generateServiceToken(req.body.appId, req.body.token, req.auth.userId)
.then(result => {
someService
.createCredentialsForUser(
req.auth.userId,
result.user.uid,
result.user.token
)
.then(result => {
return res.status(201).send(result);
});
})
.catch(error => {
return res.status(500).send({ error: `Credentials not valid - ${error}` });
});
The generateToken function is responsible to call a third party api to generate some credentials for their platform and return us the create credentials.
function generateServiceToken(appId: String, token: String, userId: String) {
return new Promise ((resolve, reject)=>{
const apiURL = `https://someapi.com/api/api.php?op=useradd&token=${token}&addr=${userId}&appid=${appId}`;
request.post(apiURL, (error, response, body) => {
const resp = JSON.parse(body);
if (resp.error) return reject(resp.error);
return resolve(resp);
});
});
}
Whereas, the someService.createCredentialsForUser function is responsible to save those credentials in database and return back the result in simple json format.
I am just stuck in stubbing someService.createCredentialsForUser function while writing the test case for happy-path
My test case is below..
describe.only("controllers/v3/some/", () => {
const c = {};
before(() => {
c.sandbox = sinon.createSandbox();
c.someServiceStub = c.sandbox
.stub(someService, "createCredentialsForUser")
.resolves(VALID_OUTPUT);
});
describe("when the request is valid", () => {
before(() => {
c.agent = setupApp(authenticationMiddleware(USER_ID));
return test(c, VALID_REQUEST_BODY);
});
it("should return 201", () => {
expect(c.response.statusCode).to.equal(201);
});
it("should call createCredentialsForUser", () => {
expect(c.stubs.createCredentialsForUser.called).to.equal(true);
});
});
});
The TestCase function is as follows..
function testCase(context, body = VALID_REQUEST_BODY) {
context.sandbox.resetHistory();
console.log(body.length);
const c = context;
return context.agent
.put(`/v3/some/`)
.send(body)
.then(r => {
c.response = r;
});
//.catch(err=>{c.response=err});
}
My someService.createCredentialsForUser function is responsible to save data into database I want to stub that that in a way that I could expect response return from generateServiceToken
I tried couples of ways which are as follows ..
First, I tried to stub that function in before() but no luck it fails with
error : IllegalArgumentError: init() must be called prior to use.
Second, I tried
c.response = c.sandbox.stub(someService, 'createCredentialsForUser').returns(Promise.resolve(r));
in my test function to stub with the value of resolved promise but no luck in this case it fails with the same error as mentioned above.

Resources