0

I am writing tests for express APIs. I use proxyquire to stub module.exports = someFunction() type of dependency.

I have a protected route, so I used proxyquire to stub auth middleware.
This auth middleware is being stubbed.

In the controller I have a model which facthes rows from db. I want to stub this one too.
As this is a method which is attached to a model UserAddress. I can directly stub it with just sinon.

like below
let findUserAddressStub = sinon.stub(UserAdress, 'findAll').resolves({ adddre1: "address1", address2: "address2"})

However above code doesn't work. findAll method is not being stubbed.

I think proxyquire is messing with scope of the express app.

I tried to stub it with proxyquire itself.
But I don't know How to stub individual methods when exported a objects?

modules.exports = {
findAll: function(){},
findOne: function(){}
}

address.test.js

const expect = require('chai').expect
const request = require('supertest')
const _ = require('lodash')
const sinon = require('sinon')
const proxyquire = require('proxyquire')
const {
    UserAddress
} = require('../../src/models')


describe('User Address /api/v1/user/addresses', function () {
    let authStub = sinon.stub().callsFake(function(req, res,next) {
        next()
    })
    authStub['@global'] = true
    const app = proxyquire('../../src/app', {
    './middlewares/auth': authStub
    });
    it('GET /api/v1/user/addresses', async () => {
        let findUserAddressStub = sinon.stub(UserAddress, 'findAll').resolves({
            id: "Z",
            address_line_1: null,
            address_line_2: null,
            city: null,
            state: "Bengaluru",
            pincode: null,
            is_deleted: false,
            landmark: null,
            map_lat: null,
            map_lng: null,
            label: null,
            service_area_id: null
        })
        const result = await request(app)
            .get('/api/v1/user/addresses')
            .set('Accept', 'application/json')
            .expect('Content-Type', /json/)
            .expect(200)
        expect(findUserAddressStub.calledOnce).to.be.true
        expect(result)
            .to.be.a('Object')
    })
})

Update
I also tried below code. But still it's not working.

 const app = proxyquire('../../src/app', {
    './middlewares/auth': authStub,
    './models/useraddress': {
        'findAll': findUserAddressStub
    }
    });

Address.js (controller)

//Models
const {
    UserAddress
} = require('../../models')

const getAddresses = async function(req, res, next) {
    const addresses = await UserAddress.findAll({
        where: {
            user_id: req.user.id,
            is_deleted: false
        },
        attributes: {
            exclude: ["user_id", "created_by", "updated_by", "created_on", "updated_on"]
        },
        ...paginate(+req.query.page, +req.query.per_page)
    })
    return res.status(200).send({
        status: true,
        message: "Successfully fetched addresses!",
        data: addresses
    })
}

When user is authenticated I nest user_id into req inside auth middleware and pass it to the route handler.

So I tried below also

 let authStub = sinon.stub().callsFake(function(req, res,next) {
        req.user = {}
        req.user.id = 3
        next()
    })

But still, findUserAddressStub is not being called.

sujeet
  • 3,480
  • 3
  • 28
  • 60

1 Answers1

1

Here is a completed working example:

app.js:

const express = require('express');
const app = express();
const auth = require('./middlewares/auth');
const { UserAddress } = require('./models');

app.get('/api/v1/user/addresses', auth, async (req, res) => {
  const addrs = await UserAddress.findAll();
  res.json(addrs);
});

module.exports = app;

./middlewares/auth.js:

module.exports = function(req, res, next) {
  next();
};

./models/userAddress.js:

module.exports = {
  findAll: function() {},
  findOne: function() {},
};

./models/index.js:

module.exports = {
  UserAddress: require('./userAddress'),
};

address.test.js:

const expect = require('chai').expect;
const request = require('supertest');
const sinon = require('sinon');
const proxyquire = require('proxyquire');

describe('User Address /api/v1/user/addresses', function() {
  it('GET /api/v1/user/addresses', async () => {
    let authStub = sinon.stub().callsFake(function(req, res, next) {
      next();
    });
    authStub['@global'] = true;
    let findUserAddressStub = sinon.stub().resolves({
      id: 'Z',
      address_line_1: null,
      address_line_2: null,
      city: null,
      state: 'Bengaluru',
      pincode: null,
      is_deleted: false,
      landmark: null,
      map_lat: null,
      map_lng: null,
      label: null,
      service_area_id: null,
    });
    const app = proxyquire('./app', {
      './middlewares/auth': authStub,
      './models': {
        UserAddress: {
          findAll: findUserAddressStub,
        },
      },
    });
    const result = await request(app)
      .get('/api/v1/user/addresses')
      .set('Accept', 'application/json')
      .expect('Content-Type', /json/)
      .expect(200);
    expect(findUserAddressStub.calledOnce).to.be.true;
    expect(result).to.be.a('Object');
  });
});

Integration test results with coverage report:

  User Address /api/v1/user/addresses
    ✓ GET /api/v1/user/addresses (2824ms)


  1 passing (3s)

----------------------|---------|----------|---------|---------|-------------------
File                  | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------------------|---------|----------|---------|---------|-------------------
All files             |   91.67 |      100 |      25 |   91.67 |                   
 60631196             |     100 |      100 |     100 |     100 |                   
  app.js              |     100 |      100 |     100 |     100 |                   
 60631196/middlewares |      50 |      100 |       0 |      50 |                   
  auth.js             |      50 |      100 |       0 |      50 | 2                 
 60631196/models      |     100 |      100 |       0 |     100 |                   
  index.js            |     100 |      100 |     100 |     100 |                   
  userAddress.js      |     100 |      100 |       0 |     100 |                   
----------------------|---------|----------|---------|---------|-------------------

source code: https://github.com/mrdulin/expressjs-research/tree/master/src/stackoverflow/60631196

Lin Du
  • 88,126
  • 95
  • 281
  • 483
  • It's not working. Getting `TypeError: Cannot read property 'id' of undefined` this comes from controller. Were I do query `UserAddress.findAll({ user_id: req.user.id})`. But if it's stubbed, it should not throw error, still It's calling and running the query. – sujeet Mar 11 '20 at 10:15
  • I will add the controller in the description. – sujeet Mar 11 '20 at 10:16
  • After I add `req.user.id` in the `authStub` I am not getting `undefined id` error anymore. But still `getUserAddressStub` is not being called. – sujeet Mar 11 '20 at 10:39