0

How do I individually implement stubs to functions invoked inside a parent function?

Assuming I have these functions (req1,req2...) that are HTTP Requests from external services that are all returning differing values, is there a way where I can apply stubs for req1 or req2 individually to mock their values?

The purpose of this is because I need to do this to test a function that relies on an OTP verification and I want to bypass said verification in order to cover all branches in my testing.

import request from 'request-promise'
const request1 = async (data) => return request({uri: "service1.com/get", method: "GET"})

const apiRequests = async (data) => {
   const req1 = await request1(data); // I want to mock this value to false
   const req2 = await request2(data); // I want to mock this value to true

   if (req1 && req2) {
    const req3 = await request3(data);
    const req4 = await request4(data);

    return "Second return"
   }
   return "First return"
}

I've always been overwhelmed whenever trying to understand the deeper levels of mocking and most of the examples I see online aren't as nested the problem I'm facing so I'm a bit puzzled about how to go on about this.

I also work in a pretty strict setup so I'm not really allowed to use any other libraries/packages outside of Loopback's built-in testing libraries.

Lin Du
  • 88,126
  • 95
  • 281
  • 483
RoboKy
  • 141
  • 1
  • 10

1 Answers1

1

You can use stub.onCall(n) API.

Defines the behavior of the stub on the nth call. Useful for testing sequential interactions.

Besides, sinon does NOT support stub a standalone function import from a package, you need to use link seams, so that we use proxyquire package to construct seams.

E.g.

apiRequest.ts:

import request from 'request-promise';

const request1 = async (data) => request({ uri: 'service1.com/get', method: 'GET' });

export const apiRequests = async (data) => {
  const req1 = await request1(data);
  const req2 = await request1(data);
  console.log(req1, req2);

  if (req1 && req2) {
    const req3 = await request1(data);
    const req4 = await request1(data);

    return 'Second return';
  }
  return 'First return';
};

apiRequest.test.ts

import proxyquire from 'proxyquire';
import sinon from 'sinon';

describe('70241641', () => {
  it('should second return', async () => {
    const rpStub = sinon.stub().onCall(0).resolves(true).onCall(1).resolves(true);
    const { apiRequests } = proxyquire('./apiRequest', {
      'request-promise': rpStub,
    });
    const actual = await apiRequests('test data');
    sinon.assert.match(actual, 'Second return');
  });

  it('should first second', async () => {
    const rpStub = sinon.stub().onCall(0).resolves(false).onCall(1).resolves(true);
    const { apiRequests } = proxyquire('./apiRequest', {
      'request-promise': rpStub,
    });
    const actual = await apiRequests('test data');
    sinon.assert.match(actual, 'First return');
  });
});

test result:

  70241641
true true
    ✓ should second return (2374ms)
false true
    ✓ should first second


  2 passing (2s)

---------------|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|---------|----------|---------|---------|-------------------
All files      |     100 |      100 |     100 |     100 |                   
 apiRequest.ts |     100 |      100 |     100 |     100 |                   
---------------|---------|----------|---------|---------|-------------------
Lin Du
  • 88,126
  • 95
  • 281
  • 483
  • What if the method I'm accessing is inside a class? I'm trying to stub a method inside a controller class and that method contains basically the apiRequests operation. How do I use proxyquire with it? – RoboKy Dec 06 '21 at 13:12
  • @RoboKy If your class method use `apiRequests` function internally, and you want to test that method, you could stub `apiRequests` instead of the `request` function from `request-promise`. But it doesn't have to be, depending on the size of the unit you're testing. The larger the unit, the more code being tested, and the more complex the test may become, the closer it becomes to an integration test – Lin Du Dec 07 '21 at 01:32