How to chain http calls with superagent/supertest?
Asked Answered
F

5

49

I am testing an express API with supertest.

I couldn't get multiple requests in a test case to work with supertest. Below is what i tried in a test case. But the test case seem to only execute the last call which is the HTTP GET.

it('should respond to GET with added items', function(done) {
    var agent = request(app);
    agent.post('/player').type('json').send({name:"Messi"});
    agent.post('/player').type('json').send({name:"Maradona"});
    agent.get('/player').set("Accept", "application/json")
        .expect(200)
        .end(function(err, res) {
            res.body.should.have.property('items').with.lengthOf(2);
            done();
    });
);

Anything i am missing here, or is there another way to chain http calls with superagent?

Farnsworth answered 13/1, 2014 at 11:12 Comment(0)
A
29

The calls are made asynchronous, so you need to use callback functions to chain them.

it('should respond to GET with added items', function(done) {
    var agent = request(app);
    agent.post('/player').type('json').send({name:"Messi"}).end(function(){
        agent.post('/player').type('json').send({name:"Maradona"}).end(function(){
            agent.get('/player')
                .set("Accept", "application/json")
                .expect(200)
                .end(function(err, res) {
                    res.body.should.have.property('items').with.lengthOf(2);
                    done();
                });
        });
    });
});
Apprehend answered 13/1, 2014 at 11:21 Comment(6)
Have a look at supertest-as-promised to reduce the nesting of callbacksSiusiubhan
Is it possible to chain them without using mocha or it ?Schismatic
supertest has native promise support since version 2.0.0Linwoodlinz
I couldn't get this solution to work. However, I could get the "async" solution (by Tim) below to work.Nettles
This answer is correct, however outdated, using async/await can give the same result with a much cleaner implementationBorlow
@Linwoodlinz The link you've added to multiple posts does not work anymore.Obit
R
69

Tried to put this in a comment above, formatting wasn't working out.

I'm using async, which is really standard and works really well.

it('should respond to only certain methods', function(done) {
    async.series([
        function(cb) { request(app).get('/').expect(404, cb); },
        function(cb) { request(app).get('/new').expect(200, cb); },
        function(cb) { request(app).post('/').send({prop1: 'new'}).expect(404, cb); },
        function(cb) { request(app).get('/0').expect(200, cb); },
        function(cb) { request(app).get('/0/edit').expect(404, cb); },
        function(cb) { request(app).put('/0').send({prop1: 'new value'}).expect(404, cb); },
        function(cb) { request(app).delete('/0').expect(404, cb); },
    ], done);
});
Ripon answered 20/9, 2014 at 5:41 Comment(3)
This should be the accepted answer. Async works fantastically in this context.Blankenship
Great approach! I extended it somewhat, check here https://mcmap.net/q/349955/-how-to-chain-http-calls-with-superagent-supertestSawfly
I get "cannot find name async". Is there a package needed for this? I can't seem to find an answer onlineFormalize
A
29

The calls are made asynchronous, so you need to use callback functions to chain them.

it('should respond to GET with added items', function(done) {
    var agent = request(app);
    agent.post('/player').type('json').send({name:"Messi"}).end(function(){
        agent.post('/player').type('json').send({name:"Maradona"}).end(function(){
            agent.get('/player')
                .set("Accept", "application/json")
                .expect(200)
                .end(function(err, res) {
                    res.body.should.have.property('items').with.lengthOf(2);
                    done();
                });
        });
    });
});
Apprehend answered 13/1, 2014 at 11:21 Comment(6)
Have a look at supertest-as-promised to reduce the nesting of callbacksSiusiubhan
Is it possible to chain them without using mocha or it ?Schismatic
supertest has native promise support since version 2.0.0Linwoodlinz
I couldn't get this solution to work. However, I could get the "async" solution (by Tim) below to work.Nettles
This answer is correct, however outdated, using async/await can give the same result with a much cleaner implementationBorlow
@Linwoodlinz The link you've added to multiple posts does not work anymore.Obit
E
16

This can be most elegantly solved with promises, and there's a really useful library to use promises with supertest: https://www.npmjs.com/package/supertest-as-promised

Their example:

return request(app)
  .get("/user")
  .expect(200)
  .then(function (res) {
    return request(app)
      .post("/kittens")
      .send({ userId: res})
      .expect(201);
  })
  .then(function (res) {
    // ... 
  });
Erring answered 3/6, 2016 at 17:59 Comment(2)
Promises are a good option here. Just need to be weary of situations where you might need to recover from a 404 somewhere in that chain.Psaltery
supertest has native promise support since version 2.0.0Linwoodlinz
T
9

I built upon Tim’s reply but used async.waterfall instead, to be able to do assert tests on the results (note: I use Tape here instead of Mocha):

test('Test the entire API', function (assert) {
    const app = require('../app/app');
    async.waterfall([
            (cb) => request(app).get('/api/accounts').expect(200, cb),
            (results, cb) => { assert.ok(results.body.length, 'Returned accounts list'); cb(null, results); },
            (results, cb) => { assert.ok(results.body[0].reference, 'account #0 has reference'); cb(null, results); },
            (results, cb) => request(app).get('/api/plans').expect(200, cb),
            (results, cb) => request(app).get('/api/services').expect(200, cb),
            (results, cb) => request(app).get('/api/users').expect(200, cb),
        ],
        (err, results) => {
            app.closeDatabase();
            assert.end();
        }
    );
});
Taw answered 19/7, 2017 at 18:35 Comment(1)
You saved me a lot of time. Currently, it's the best answer.Gerous
S
0

This worked for me :

const agent = request(server.app);
const fn = (counter) => {
  agent.get('/').end((_error, res) => {
    if (res.status !== 200) {
      done();
      expect(counter).toEqual(20);
    } else
      fn(counter + 1);
  })
}

fn(1);
Scribble answered 22/7, 2022 at 3:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.