Sinon: private ๋ฉค๋ฒ„๊ฐ€ ์žˆ๋Š” ํด๋ž˜์Šค์—์„œ createStubInstance๋ฅผ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2019๋…„ 01์›” 04์ผ  ยท  3์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: sinonjs/sinon

๋ฒ„๊ทธ ์„ค๋ช…
I sinon.createStubInstance(X) ์—ฌ๊ธฐ์„œ X๊ฐ€ ํ•˜๋‚˜ ์ด์ƒ์˜ private ๋ฉค๋ฒ„๊ฐ€ ์žˆ๋Š” ํด๋ž˜์Šค์ธ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ์ˆ˜์‹ ๋ฉ๋‹ˆ๋‹ค.

'SinonStubbedInstance ์œ ํ˜•์˜ ์ธ์ˆ˜'์€(๋Š”) 'Foo' ์œ ํ˜•์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
'SinonStubbedInstance' ์œ ํ˜•์— '_private' ์†์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค.' ํ•˜์ง€๋งŒ '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);
  });
});

์žฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด
๋™์ž‘์„ ์žฌํ˜„ํ•˜๋Š” ๋‹จ๊ณ„:

  1. mocha์™€ sinon์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์„ค์ •ํ•˜์‹ญ์‹œ์˜ค.
  2. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์‚ฌ์–‘ ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ
  3. ์œ„์˜ ์ฝ”๋“œ ์Šค๋‹ˆํŽซ์„ ๋ถ™์—ฌ๋„ฃ๊ณ  ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  4. printSpeech(mockFoo) ๋ผ์ธ์—์„œ ์˜ค๋ฅ˜ ๋ณด๊ธฐ

์˜ˆ์ƒ๋˜๋Š” ํ–‰๋™
๋‚˜๋Š” ์ด๊ฒƒ์ด ์˜ค๋ฅ˜์—†์ด ์ž‘๋™ํ•˜๊ธฐ๋ฅผ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” _private()์— ๋Œ€ํ•ด ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š๊ณ  ๊ทธ ๋™์ž‘์„ ์ง์ ‘ ํ…Œ์ŠคํŠธํ•  ์ƒ๊ฐ๋„ ์—†์ง€๋งŒ ๋นŒ๋“œ ์‹œ๊ฐ„ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. Jasmine์˜ ๊ตฌํ˜„์—๋Š” ๋‹ค๋ฅธ ๊ฒฐํ•จ์ด ์žˆ์ง€๋งŒ Jasmine์€ jasmine.createSpyObj ๋กœ ์ด ๊ฒฝ์šฐ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

์ปจํ…์ŠคํŠธ(๋‹ค์Œ ์ •๋ณด๋ฅผ ์ž‘์„ฑํ•˜์‹ญ์‹œ์˜ค):

  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฒ„์ „: sinon: 7.2.2, @types/sinon: 7.0.3
  • ํ™˜๊ฒฝ: WSL Ubuntu 16.04, ๋…ธ๋“œ 10

  • ์‚ฌ์šฉ ์ค‘์ธ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ: Mocha 5.2.0

ํŽธ์ง‘: ์—ฌ๊ธฐ stackblitz๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค https://stackblitz.com/edit/typescript-nfbgno?file=index.ts

๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰ ์ค‘์ด๊ธฐ ๋•Œ๋ฌธ์— process.stdout์— ๋Œ€ํ•ด ๋ถˆํ‰ํ•˜์ง€๋งŒ ์‹ค์ œ ๋ฌธ์ œ๋Š” ์ปดํŒŒ์ผ ์‹œ๊ฐ„์ด๋ฏ€๋กœ ์ค‘์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์ด๊ฒƒ์ด ํ™•์‹คํ•œ ํ•ด๊ฒฐ์ฑ…์€ ์•„๋‹ˆ์ง€๋งŒ ๋‹ค์Œ ๊ธฐ๋Šฅ์œผ๋กœ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

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>;
}

๋ถ„๋ช…ํžˆ ์ด๊ฒƒ์€ ๊ฐœ์ธ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด ๋ž˜ํผ ํ•จ์ˆ˜๋ฅผ โ€‹โ€‹์‚ฌ์šฉํ•˜๋ฉด ํด๋ž˜์Šค์˜ ๋ชจ๋“  ๊ณต์šฉ ํ•จ์ˆ˜/๋ฉค๋ฒ„์™€ sinon์˜ ์Šคํ… ๋„์šฐ๋ฏธ ํ•จ์ˆ˜ ๋ฐ StubbedClass์— ๋Œ€ํ•œ ์ฝ”๋“œ ์™„์„ฑ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ํ…Œ์ŠคํŠธ์—์„œ T๋กœ ์‚ฌ์šฉํ•  ์œ ํšจํ•œ ์œ ํ˜•์ž…๋‹ˆ๋‹ค.

๋ชจ๋“  3 ๋Œ“๊ธ€

์ด๊ฒƒ์€ Typescript ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. Typescript๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์„ ์กฐ์‚ฌํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. Typescript๊ฐ€ ์–ด๋–ป๊ฒŒ๋“  private ๋ฉค๋ฒ„๋ฅผ ์ง€์›ํ•œ๋‹ค๋Š” ๊ฒƒ์€ ํ•ด๋‹น ์œ ํ˜• ์‹œ์Šคํ…œ์˜ ์†์„ฑ์ด๋ฉฐ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ตฌํ˜„๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Typescript๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋‚˜๋Š” ์ข…์ข… Typescript ํ”Œ๋ ˆ์ด๊ทธ๋ผ์šด๋“œ๋กœ ์ด๋™ํ•˜์—ฌ ์ถœ๋ ฅ์„ ๊ฒ€์‚ฌํ•˜์—ฌ ์–ด๋–ค ์ž‘์—…์„ ์ˆ˜ํ–‰ํ–ˆ๋Š”์ง€ ์ดํ•ดํ•จ์œผ๋กœ์จ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์ด ๋‹น์‹ ์ด ํ•ด์•ผ ํ•  ์ผ์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ํ™•์‹คํ•œ ํ•ด๊ฒฐ์ฑ…์€ ์•„๋‹ˆ์ง€๋งŒ ๋‹ค์Œ ๊ธฐ๋Šฅ์œผ๋กœ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

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>;
}

๋ถ„๋ช…ํžˆ ์ด๊ฒƒ์€ ๊ฐœ์ธ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด ๋ž˜ํผ ํ•จ์ˆ˜๋ฅผ โ€‹โ€‹์‚ฌ์šฉํ•˜๋ฉด ํด๋ž˜์Šค์˜ ๋ชจ๋“  ๊ณต์šฉ ํ•จ์ˆ˜/๋ฉค๋ฒ„์™€ sinon์˜ ์Šคํ… ๋„์šฐ๋ฏธ ํ•จ์ˆ˜ ๋ฐ StubbedClass์— ๋Œ€ํ•œ ์ฝ”๋“œ ์™„์„ฑ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ํ…Œ์ŠคํŠธ์—์„œ T๋กœ ์‚ฌ์šฉํ•  ์œ ํšจํ•œ ์œ ํ˜•์ž…๋‹ˆ๋‹ค.

@pauloavelar ๋‹ต๋ณ€์ด ์—ฌ์ „ํžˆ ๊ถŒ์žฅ๋˜๋Š” ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๊นŒ?

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰