Express JavaScript Supertest Expect specific json fields - javascript

I'm attempting to write some unit tests for API endpoints, and decided on JavaScript Express w/ Supertest. I've got the basic downs, but running into issues checking the response for a specific field. I want to parse the body and check if an expected field returns and has the correct value. Most everything I've seen online uses this method, but when I try it always passes, even when I enter values I know don't exist in the JSON. Any advice? Here is my code snippet:
describe('GET category', function () {
it('response w/ only injury returned', function () {
request('endpoint')
.get('path')
.set('header', 'token')
.expect(200)
.then(response => {
console.assert(response.body, "Baseball")
})
})
});
I have also tried changing the .then to .expect, with same results. If I do response.body.specificFieldinBody I get similar results. Any help?

You can do with your way - use .then syntax, but I think use Supertest assertion syntax will be good.
This mean, use response.body.should.have.property("Baseball"); instead of console.assert(response.body, "Baseball") (Baseball is your special field).
Or, My suggestion is creating a re-use code: Put a assertion callback function to a next expects section.
const isIncludeField = function (fieldName) {
return function (res) {
res.body.should.have.property(fieldName);
};
}
describe('GET category', function () {
it('response w/ only injury returned', function () {
request('endpoint')
.get('path')
.set('header', 'token')
.expect(200)
.expect(isIncludeField('Baseball')) //
.end(done); // call done callback
})
});

Related

How to chain my promises - Fetch not returning desired results

Fetch Promises Not Working For Me
I am a newbie when it comes to using Javascript Promises. In my current project, I believe Promises are a candidate for the task I am trying to achieve. The project is simple, it is an online form to submit a quote (or estimate) to a customer.
I am working on the page that will display a Quote that has already been submitted - ( view-quote.html )
Here is the task broken down:
Given a quote_id (url param), I want to query the QUOTES table.
In those results are a customer id.
Finally, I want to query the ITEMS table using the quote_id.
Synchronous or Asynchronous ?
Since one query depends upon another, I thought I could use the Fetch statement, which is "then-able".
I THINK I am supposed to use asynchronous code to achieve this task, right? I'm not sure though. Maybe traditional "callbacks" are what I need?
My Current Non-Working Code...
Here is as far as I got. I pieced this together by following tips and tutorials I found on Stack's site.
var getQuote = function() {
fetch(apiQuoteUrl+"GL555") // for testing, I hardcoded a quote num
.then(status)
.then(json)
.then(function(data) {
// WORKS - this DOES return me the client id
//alert(data[0].client_id);
gClient_id = data[0].client_id; // move to global var
}).catch(function(error) {
console.log('Request failed', error);
});
};
// This function takes one parameter, the client id
// which comes from the fetch call above.
var getClient = function(clientId) {
fetch()
.then(status)
.then(json)
.then(function(data) {
// TO BE DETERMINED, NOT SURE
// WHAT TO PUT HERE
}).catch(function(error) {
console.log('Request failed', error);
});
};
var getItems = function() {
fetch()
.then(status)
.then(json)
.then(function(data) {
// TO BE DETERMINED, NOT SURE
// WHAT TO PUT HERE
}).catch(function(error) {
console.log('Request failed', error);
});
};
function status(response) {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response.statusText))
}
}
function json(response) {
return response.json()
}
What to do next?
Ok, I have the framework put in place. But, I don't know to proceed. I have followed dozens of tutorials on Fetch and Promises, but they don't quite fit my needs, therefore, I am stil stuck.
Right Track?
So I am now concerned that I am making this harder than it needs to be. Am I even on the right track by using promises?
Thank you for looking. Your advice/code help is really appreciated.
John
They weren't working for me either!
Hello! I believe I recently had the same problem as you: "Chaining Promises"
A quick search brought me here, and I was able to resolve my problems.
The basics are, you need to return a value from a promise in order to .then() it.
So in the case of fetch() try something like this
var getQuote = function() {
fetch(apiQuoteUrl + "GL555")
.then(status => return status.json())
.then(data => {
alert(data[0].client_id);
gClient_id = data[0].client_id;
}).catch(error => {
console.log('Request failed', error);
});
};

Jest - TypeError: response.json is not a function

We are unit testing a React-Native application (using Jest) which does various API calls using fetch.
We have mocked calls to fetch in our API call functions in order to test them. This works well so far. We also have functions that combine these API calls and operate some logic on them.
For example, here is one function that, given a token, will get the related user's first project (project[0]) and return the list of items from this project.
export async function getAllItems(token) {
try {
const response = await getCurrentUser(token); // fetch called inside
const responseJson = await response.json();
const allItemsResp = await getAllItemsFromSpecificProject(
token,
responseJson.projectIds[0],
); // fetch called inside
return await allItemsResp.json();
} catch (error) {
console.log(error);
return null;
}
}
Both functions getCurrentUser and getAllItemsFromSpecificProject are simple fetch calls and are currently mocked properly. Here one test that tries to test the getAllItems function:
it('Gets all items', async () => {
getAccessTokenMockFetch();
const token = await getAccessToken('usherbrooke#powertree.io', 'test!23');
getAllItemsMockFetch();
const items = await getAllItems(token.response.access_token);
expect(items.response.length).toEqual(3);
});
For clarity, here is how getAccessTokenMockFetch is done. getAllItemsMockFetch is almost identical (with different data in the response):
function getAccessTokenMockFetch() {
global.fetch = jest.fn().mockImplementation(() => {
promise = new Promise((resolve, reject) => {
resolve(accepted);
});
return promise;
});
}
where accepted contains the JSON content of a successful call. When we run this test, we get the following error:
TypeError: Cannot read property 'response' of null
And we console.log this one in the catch:
TypeError: response.json is not a function
which explains why response is null. It seems the json() call is not understood and I don't know how to mock it. I have done tons of research on Stack Overflow and beyond, but we have found nothing that helps me understand how to solve this issue. This might indicate that I am going the wrong way about this, which is quite possible since I'm new to JavaScript, React Native, and Jest.
One thing to try is giving it a fake json to call, like this:
const mockFetch = Promise.resolve({ json: () => Promise.resolve(accepted) });
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);

Jest testing with Node - Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout

I'm starting to test my code with Jest, and I can't make a seemingly simple test to pass. I am simply trying to check if what I receive from a Maogoose database request is an object.
The function fetchPosts() is working because I hooked it up with a React frontend and it is displaying the data correctly.
This is my function fetchPosts():
module.exports = {
fetchPosts() {
return new Promise((resolve, reject) => {
Posts.find({}).then(posts => {
if (posts) {
resolve(posts)
} else {
reject()
}
})
})
}
}
And my test:
it('should get a list of posts', function() {
return posts.fetchPosts().then(result => {
expect(typeof result).toBe('object')
})
})
This makes the test fail, and Jest says
'Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.'
QUESTION: How can I make this test pass?
You can expect asynchronous results using resolves, as shown in the Jest documentation.
In your case:
it('should get a list of posts', function() {
const result = posts.fetchPosts();
expect(result).resolves.toEqual(expect.any(Object));
})
…although I have a suspicion your list of posts is actually an array, so you probably want this:
it('should get a list of posts', function() {
const result = posts.fetchPosts();
expect(result).resolves.toEqual(expect.any(Array));
})
Another tip: You don't need to wrap the body of your fetchPost in an additional promise, you can simply return the promise you get from Posts.find and add a then to it, like this:
module.exports = {
fetchPosts() {
return Posts.find({}).then(posts => {
if (posts) {
return posts;
}
throw new Error('no posts'); // this will cause a promise rejection
})
}
}
It's also highly possible that you're not getting a response back from the DB at all from your test suite. Test suite's can call different environmental variables / configs that lead to different calls. This error can also be seen if no response is returned, as in - if someone blocks your IP from connecting, on and on.
Also if you are simply looking to increase the timeout, then you can do that by setting
jest.setTimeout(10000);
You can use this statement in beforeEach if you want to change the timeout for all your tests in that describe block or in the test/it/spec block if you want it for a single test.
For me none of the above worked so I tried older version of jest and it worked
npm i -D jest#25.2.7.
if you are using it with typescript make sure to degrade ts-jest as well
npm i -D jest#25.2.7 ts-jest#25.3.1

Spy method from dynamically obtained object

I use mongoose and I have login function, that tries to find user and then valid his password (I will not describe here all logic of this function, but only major parts to show you my problem).
function login(req, res) {
...
User.findOne(...)
.then((user) => {
user.validPassword(...);
...
});
}
I have defined a User model that contains a validPassword method and all work fine but I have trouble to spy validPassword method.
I use Jasmine to my tests and I tried to do this in this way:
const user = new User(...);
spyOn(user, 'validPassword').and.callThrough();
expect(user.validPassword).toHaveBeenCalled(); // was never called :(
And of course I called login function. When I test findOne method in this way, it works fine, but it is simpler because I call method from User constructor:
spyOn(User, 'findOne').and.callThrough();
expect(User.findOne).toHaveBeenCalled(); // this works fine!
I think my problem is related to different instances, because findOne method resolve a new user object for me and in the tests I create a second one, what is absolutely different object, but I'm not sure about this conjecture.
Can you tell me how to repair this?
Ok I fixed this.
1) I found solution of my problem here
2) I noticed that findOne method returns promise and then I do async task, so I had to check it with 'done' function before start testing.
function login(req, res) {
...
return User.findOne(...)
.then((user) => {
user.validPassword(...);
...
});
}
In jasmine:
beforeAll((done) => {
spyOn(User.prototype, 'validPassword').and.callThrough();
login(...).then(() => done());
}
it('calls validPassword', () => {
expect(User.prototype.validPassword).toHaveBeenCalled(); // works :)
});

What is the easiest way to wrap Promise in ES6?

I'm using a promise-based package (Axios) for making HTTP requests. So, I have a code like this:
axios.all(/*many generated requests*/).then((res) => {
//success handler
}).catch((err) => {
//error handler
});
I want to write a simple wrapper which generates and sends all the requests, but still has the same syntax. It will make the code above look like:
manyReqsWrapper(args).then((res) => {
//success handler
}).catch((err) => {
//error handler
});
How can I do this?
Promises are simple values and can be returned from functions like everything else. You seem to be looking for
function mayReqsWrapper(args) {
return axios.all(/* whatever you need */);
}

Categories