6

I am having issues when testing that the original method (from the base class), is called with some parameters, when testing an extended class. The class that I want to test is:

// ApiDataSource.js
import { RESTDataSource } from "apollo-datasource-rest";

export default class ApiDataSource extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = 'test';
  }

  //We add the authorization params to the get method
  async get(path, params = {}, init = {}) {
    return super.get(
      path,
      {
        ...params,
        app_id: "test app id",
        app_key: "test app key",
      },
      init
    );
  }
}

And basically, I would want to mock super.get() to assert that when ApiDataSource.get() is called, the super method is called with the authorization parameters.

Something like:

// ApiDataSource.test.js
import ApiDataSource from './ApiDataSource'

// ...
test("adds the authorization parameters to the get call", async () => ({
  const class = new ApiDataSource();
  await class.get("test");

  expect(mockedSuperGet).toHaveBeenCalledWith("test", {app_id: "test app id", app_key: "test app key"})
});

Any idea how to do that? have tried jest.mock() jest.spyOn and so on and I can't seem to get it...

CodeSandbox: https://codesandbox.io/s/example-test-api-data-source-6ldpk?file=/src/ApiDataSource.test.ts

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Albert Alises
  • 1,018
  • 1
  • 8
  • 19
  • Sure, just mock/spy on `RESTDataSource.prototype.get`? – Bergi Dec 21 '20 at 10:53
  • Could you provide an example on how to do that @Bergi (based on the above code)? – Albert Alises Dec 21 '20 at 10:56
  • I don't have one at hand, no, but see their documentation. Can you maybe [edit] your question to include your attempts? – Bergi Dec 21 '20 at 10:57
  • Have quickly compiled this @Bergi : https://codesandbox.io/s/example-test-api-data-source-6ldpk?file=/src/ApiDataSource.test.ts there seems to be an issue with jest.mock() but maybe you get the idea... have also tried mocking the whole class with `jest.mock("apollo-datasource-rest", () => ({ RESTDataSource: jest.fn()})` or whatever but didn't have any success either... – Albert Alises Dec 21 '20 at 11:20
  • You don't mock the superclass. The inherited behaviour is *part of the behaviour*. Mock *collaborators* instead. – jonrsharpe Dec 21 '20 at 11:35
  • That's interesting jonrsharpe. How would you test it in this case? – Albert Alises Dec 21 '20 at 11:53
  • There's no single correct approach to this problem. It doesn't make sense to mock a single parent method here, more like the whole parent, especially if it's third party and produces side effects like this lib. One approach would be to not mock parent class but side effects instead, there are third party libs for that too, for this one it's probably https://github.com/NickTomlin/apollo-datasource-rest-testing . You can additionally *spy* on internal calls but only as a way to track down mistakes in your own code more easily. – Estus Flask Dec 21 '20 at 12:20

1 Answers1

13

You can use jest.spyOn(object, methodName) to create a mock function for RESTDataSource.prototype.get method.

E.g.

ApiDataSource.js:

import { RESTDataSource } from 'apollo-datasource-rest';

export default class ApiDataSource extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = 'test';
  }

  async get(path, params = {}, init = {}) {
    return super.get(
      path,
      {
        ...params,
        app_id: 'test app id',
        app_key: 'test app key',
      },
      init
    );
  }
}

ApiDataSource.test.js:

import { RESTDataSource } from 'apollo-datasource-rest';
import ApiDataSource from './ApiDataSource';

describe('65391369', () => {
  test('adds the authorization parameters to the get call', async () => {
    const mockedSuperGet = jest.spyOn(RESTDataSource.prototype, 'get').mockResolvedValueOnce('fake data');
    const apiDataSource = new ApiDataSource();
    await apiDataSource.get('test');
    expect(mockedSuperGet).toHaveBeenCalledWith('test', { app_id: 'test app id', app_key: 'test app key' }, {});
    mockedSuperGet.mockRestore();
  });
});

unit test result:

 PASS  examples/65391369/ApiDataSource.test.js (5.273 s)
  65391369
    ✓ adds the authorization parameters to the get call (4 ms)

------------------|---------|----------|---------|---------|-------------------
File              | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
------------------|---------|----------|---------|---------|-------------------
All files         |     100 |      100 |     100 |     100 |                   
 ApiDataSource.js |     100 |      100 |     100 |     100 |                   
------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        6.28 s
Lin Du
  • 88,126
  • 95
  • 281
  • 483
  • Some commentary on whether this is good practice might be helpful. Just because you *can* doesn't necessarily mean you *should*! – jonrsharpe Dec 21 '20 at 11:47