5

I'm trying to test for the presence of some api response properties that I want to require across all tests (a status and data property).

Here's a generic test that asserts the desired properties in a supertest expect() method:

    it('should create a widget', done => {
        let status = 200;
        request(test_url)
            .post('/api/widgets')
            .set('Authorization', `Bearer ${token}`)
            .send({
                sku: my_widget_data.sku,
                name: my_widget_data.name,
                description: ''
            })
            .expect(res => {
                assert(
                    Object.keys(res.body).includes('status'),
                    '`status` is a required field'
                );
                assert(
                    Object.keys(res.body).includes('data'),
                    '`data` is a required field'
                );
                assert.strictEqual(res.body.status, status);
                assert.strictEqual(res.status, status);
            })
            .end((err, res) => {
                if (err) return done(err);
                done();
            });
    });

This expect() behavior is going to be common to almost all my tests.

How can I extract the expect() behavior to DRY up my tests, while still passing arbitrary status numbers?

doub1ejack
  • 10,627
  • 20
  • 66
  • 125
  • You are passing `expect` a function. There's no reason you can't save that function elsewhere and pass a reference to this one function into all the tests. – Mark Mar 25 '19 at 16:42
  • @MarkMeyer That's true, but `expect()` expects a function with a signature of `(res) => any`. I'm not sure how to pass my `status` into that function. – doub1ejack Mar 25 '19 at 16:50
  • Hmm…I see what you mean. – Mark Mar 25 '19 at 16:56
  • I feel like there's a way to use currying or partial functions to do this, but I can't quite figure it out... – doub1ejack Mar 25 '19 at 17:03
  • Here's the way: `const correctStatus = function ( status, res ) { /* ... */ };` and `const status200 = correctStatus.bind( null, 200 );` then you can do `.expect( status200 )`. – Igwe Kalu Apr 01 '19 at 21:09
  • if you used chai to make assertions, you could [extend it using writing custom plugins](https://www.chaijs.com/api/plugins/#method_addmethod) in a clean manner – Gonzalo.- Apr 01 '19 at 22:03

1 Answers1

6

You could extrapolate the function that expect() calls into another one which returns a function you pass status into:

export function statusCheck(status) {
  return res => {
    assert(
      Object.keys(res.body).includes("status"),
      "`status` is a required field",
    )
    assert(Object.keys(res.body).includes("data"), "`data` is a required field")
    assert.strictEqual(res.body.status, status)
    assert.strictEqual(res.status, status)
  }
}

Now in your original file, you could call:

.expect(statusCheck(200))

Here's a snippet showing how it works as well:

// Ignored since status is scoped below
const status = 400

// Returns your (res) => {} function, uses status
function statusCheck(status) {
  return res => {
    console.log(`Desired status number is ${status}`)
    if(res.status === status) console.log(`Response: ${res.status}, It worked!`)
    else console.log(`Response: ${res.status}, It failed!`)
  }
}

// Testing if it works with a mockup
const thisGoesInsideExpect = statusCheck(200)
const res = {status: 200}
thisGoesInsideExpect(res)
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
leander
  • 534
  • 1
  • 4
  • 18