0

I'm trying to unit test a module that looks something like this

import { Countdown } from "./database/orm";

export class PersistentTimer {
    protected constructor(...) { ... }
    
    // This object requires writing to a database, so I protect the constructor and
    // force construction through an async interface
    public static async create(...) {
        const entity = new Countdown();
        // do stuff
        await entity.save();
        return new PersistentTimer(entity);
    }
}

I'm looking for something like jest.mock within the mocha ecosystem to mock the Countdown module since it has a side effect so that I can unit test this module. Sinon seems to only deal with functions, properties on objects, or object instances passed to function calls.

Lin Du
  • 88,126
  • 95
  • 281
  • 483
Kevin Lu
  • 303
  • 1
  • 4
  • 13
  • Does this answer your question? [How to mock npm module with sinon/mocha](https://stackoverflow.com/questions/57749627/how-to-mock-npm-module-with-sinon-mocha) – jonrsharpe Dec 21 '20 at 21:04

1 Answers1

1

You can stub out ./database/orm module with link seams.

This is the CommonJS version, so we will be using proxyquire to construct our seams.

E.g.

main.ts:

import { Countdown } from './database/orm';

export class PersistentTimer {
  protected constructor(entity: Countdown) {}

  public static async create() {
    const entity = new Countdown();
    await entity.save();
    return new PersistentTimer(entity);
  }
}

main.test.ts:

import sinon from 'sinon';
import proxyquire from 'proxyquire';
import { expect } from 'chai';

describe('65399764', () => {
  it('should pass', async () => {
    const countdownInstanceStub = {
      save: sinon.stub(),
    };
    const CountdownStub = sinon.stub().returns(countdownInstanceStub);
    const { PersistentTimer } = proxyquire('./main.ts', {
      './database/orm': {
        Countdown: CountdownStub,
      },
    });
    const persistentTimer = await PersistentTimer.create();
    sinon.assert.calledOnce(CountdownStub);
    sinon.assert.calledOnce(countdownInstanceStub.save);
    expect(persistentTimer).to.be.instanceOf(PersistentTimer);
  });
});

unit test result:

  65399764
    ✓ should pass (1848ms)


  1 passing (2s)

-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   85.71 |      100 |   66.67 |   85.71 |                   
 65399764          |     100 |      100 |     100 |     100 |                   
  main.ts          |     100 |      100 |     100 |     100 |                   
 65399764/database |      50 |      100 |       0 |      50 |                   
  orm.ts           |      50 |      100 |       0 |      50 | 3                 
-------------------|---------|----------|---------|---------|-------------------
Lin Du
  • 88,126
  • 95
  • 281
  • 483
  • Thanks for the link again. I found that proxyquire ends up erasing the types entirely (the imported module has type any, which messes up some test applications) but I was able to use the general principle of link seams where this fell short. – Kevin Lu Jan 01 '21 at 17:25