Sinon: Cannot createStubInstance on class with private members

Created on 4 Jan 2019  ·  3Comments  ·  Source: sinonjs/sinon

Describe the bug
if I sinon.createStubInstance(X) where X is a class with at least one private member, I receive errors like:

Argument of type 'SinonStubbedInstance' is not assignable to parameter of type 'Foo'.
Property '_private' is missing in type 'SinonStubbedInstance' but required in type 'Foo'.

describe('SinonTest', () => {
  class Foo {
    public talk() {
      return 'hi';
    }
    private _private() {
      return null;
    }
  }

  function printSpeech(foo: Foo) {
    console.log(foo.talk());
  }
  it('should allow you to pass a stub as a the original', () => {
    const mockFoo = sinon.createStubInstance(Foo);

    printSpeech(mockFoo);
  });
});

To Reproduce
Steps to reproduce the behavior:

  1. Set up a project with mocha and sinon.
  2. Create a unit test spec file
  3. Paste the above snippet of code and add any imports required
  4. See error on line with printSpeech(mockFoo)

Expected behavior
I would expect this to work without error. I don't care about _private(), nor do I intend to directly test its behavior, but it should not result in a build time error. Jasmine supports this case with jasmine.createSpyObj, although jasmine's implementation has other flaws.

Context (please complete the following information):

  • Library version: sinon: 7.2.2, @types/sinon: 7.0.3
  • Environment: WSL Ubuntu 16.04, Node 10

  • Other libraries you are using: Mocha 5.2.0

edit: here's a stackblitz https://stackblitz.com/edit/typescript-nfbgno?file=index.ts

It complains about process.stdout because it's running in browser, but that's unimportant since the actual problem is compile-time

Most helpful comment

This is not a definitive solution, but I managed to fix this problem with the following function:

import { createStubInstance, StubbableType, SinonStubbedInstance, SinonStubbedMember } from 'sinon';

export type StubbedClass<T> = SinonStubbedInstance<T> & T;

export function createSinonStubInstance<T>(
  constructor: StubbableType<T>,
  overrides?: { [K in keyof T]?: SinonStubbedMember<T[K]> },
): StubbedClass<T> {
  const stub = createStubInstance<T>(constructor, overrides);
  return stub as unknown as StubbedClass<T>;
}

Obviously, this does not implement any private methods, and as such you cannot (and should not by design) call them. By using this wrapper function, you get code completion to all public functions/members in your class as well as sinon's stub helper functions, and StubbedClass is a valid type to be used as T in your tests.

All 3 comments

This is a Typescript issue. We are not going to use time looking into this, as we don't support Typescript explicitly. That Typescript somehow has support for private members is a property of its type system and could be implemented in number of ways. When I used Typescript I often resolved such issues by going to the Typescript playground and inspecting the output to understand what it did. That's what you need to do as well.

This is not a definitive solution, but I managed to fix this problem with the following function:

import { createStubInstance, StubbableType, SinonStubbedInstance, SinonStubbedMember } from 'sinon';

export type StubbedClass<T> = SinonStubbedInstance<T> & T;

export function createSinonStubInstance<T>(
  constructor: StubbableType<T>,
  overrides?: { [K in keyof T]?: SinonStubbedMember<T[K]> },
): StubbedClass<T> {
  const stub = createStubInstance<T>(constructor, overrides);
  return stub as unknown as StubbedClass<T>;
}

Obviously, this does not implement any private methods, and as such you cannot (and should not by design) call them. By using this wrapper function, you get code completion to all public functions/members in your class as well as sinon's stub helper functions, and StubbedClass is a valid type to be used as T in your tests.

Is the @pauloavelar answer still the recommended workaround ?

Was this page helpful?
0 / 5 - 0 ratings