๋๋ ์ฝ๊ณ ๋ถ๋ช ํด์ผ ํ๋ค๊ณ ์๊ฐํ์ง๋ง ์ด๋ค ์ด์ ์์์ธ์ง ์ ์ ์๋ ๋ฌธ์ ๋ก ์ด๋ ค์์ ๊ฒช๊ณ ์์ต๋๋ค.
๋ชจ๋์ด ์์ต๋๋ค. ์ฌ๋ฌ ๊ธฐ๋ฅ์ ๋ด๋ณด๋
๋๋ค. ๋ค์์ myModule.js
.
export function foo() {...}
export function bar() {...}
export function baz() {...}
ํ ์คํธ๋ฅผ ์ํด ๋ชจ๋์ ์ ๊ธ ํด์ ํฉ๋๋ค.
jest.unmock('./myModule.js');
๊ทธ๋ฌ๋ ๋ด ๋ฐฑ์๋์ ๋ํ ์์ฝ์ค ํธ์ถ์ ๋ง๋ค๊ธฐ ๋๋ฌธ์ foo
์กฐ๋กฑํด์ผ ํฉ๋๋ค. ์ด ํ์ผ์ ๋ชจ๋ ํจ์๊ฐ ์กฐ๋กฑ๋์ง ์์ ์ํ๋ก ์ ์ง๋๊ธฐ๋ฅผ ์ํฉ๋๋ค. foo
๋ฅผ ์์ํ๊ณ ์กฐ๋กฑ์ ๋ฐ๊ณ ์ถ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ bar
๋ฐ baz
ํจ์๋ ๋ด๋ถ์ ์ผ๋ก foo
ํธ์ถํ๋ฏ๋ก ๋ด ํ
์คํธ๊ฐ bar()
ํธ์ถํ๋ฉด ์กฐ๋กฑ๋์ง ์์ bar
์กฐ๋กฑ foo
.
unmock
๋ฐ mock
ํธ์ถ์ด ์ ์ฒด ๋ชจ๋์์ ์๋ํ๋ jest ๋ฌธ์์ ๋ํ๋ฉ๋๋ค. ํน์ ๊ธฐ๋ฅ์ ์ด๋ป๊ฒ ์กฐ๋กฑํ ์ ์์ต๋๊น? ์ ์ ํ๊ฒ ํ
์คํธํ ์ ์๋๋ก ๋ด ์ฝ๋๋ฅผ ๋ณ๋์ ๋ชจ๋๋ก ์์๋ก ๋๋๋ ๊ฒ์ ์ด๋ฆฌ์์ ์ผ์
๋๋ค.
์ฌ์ธต ๋ถ์์์ jest-mock์ ์ ์ฒด ๋ชจ๋์ ๋ํ AST๋ฅผ ์์ฑํ ๋ค์ ํด๋น AST๋ฅผ ์ฌ์ฉํ์ฌ ์๋ณธ์ ๋ด๋ณด๋ด๊ธฐ๋ฅผ ์ค์ํ๋ ๋ชจ์ ๋ชจ๋์ ์์ฑํ๋ ๊ฒ์ผ๋ก ๋ณด์ ๋๋ค. https://github.com/facebook/jest/tree/master/packages / ๋๋ด ์กฐ๋กฑ
Python์ ๋ชจ์(https://docs.python.org/3/library/unittest.mock-examples.html)์ ๊ฐ์ ๋ค๋ฅธ ํ ์คํธ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ฉด ํน์ ๊ธฐ๋ฅ์ ๋ชจ์ํ ์ ์์ต๋๋ค. ์ด๊ฒ์ ๊ธฐ๋ณธ์ ์ธ ํ ์คํธ ๊ฐ๋ ์ ๋๋ค.
๋ชจ๋์ ์ผ๋ถ๋ฅผ ์กฐ๋กฑํ๋ ๊ธฐ๋ฅ์ ์ ๊ทน ๊ถ์ฅํฉ๋๋ค. jest-mock์ ์กฐ๊ฑด๋ถ๋ก mocking์์ ๋ด๋ณด๋ด๊ธฐ๋ฅผ ๋ฌด์ํ๊ณ ์๋ ๊ตฌํ์ ์ฐธ์กฐํ๋๋ก ๋ณ๊ฒฝ๋์ด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๋ ํ ์์์ด:
jest.unmock('./myModule.js');
const myModule = require('myModule');
myModule.foo = jest.fn();
http://facebook.github.io/jest/docs/api.html#mock -functions ์ฐธ์กฐ
require
์๋ ๋ฐฉ์์ ๋ํ ๊ทผ๋ณธ์ ์ธ ์คํด๊ฐ ์๋ ๊ฒ ๊ฐ์ต๋๋ค. require()
๋ฅผ ํธ์ถํ๋ฉด ๋ชจ๋์ ์ธ์คํด์ค๋ฅผ ์ป์ง ๋ชปํฉ๋๋ค. ๋ชจ๋์ ๊ธฐ๋ฅ์ ๋ํ ์ฐธ์กฐ๊ฐ ์๋ ๊ฐ์ฒด๋ฅผ ์ป์ต๋๋ค. ํ์ํ ๋ชจ๋์ ๊ฐ์ ๋ฎ์ด์ฐ๋ฉด ์์ ์ ์ฐธ์กฐ๋ฅผ ๋ฎ์ด์ฐ์ง๋ง _๊ตฌํ์ ์๋ ์ฐธ์กฐ๋ฅผ ์ ์งํฉ๋๋ค_.
๊ทํ์ ์์์ myModule.foo()
๋ฅผ ํธ์ถํ๋ฉด ์, ๋ชจ์ ๋ฒ์ ์ ํธ์ถํฉ๋๋ค. ๊ทธ๋ฌ๋ ๋ด๋ถ์ ์ผ๋ก foo()
๋ฅผ ํธ์ถํ๋ myModule.bar()
๋ฅผ ํธ์ถํ๋ฉด ์ฐธ์กฐํ๋ foo
_๋ฎ์ด์ด ๋ฒ์ ์ด ์๋๋๋ค_. ๋น์ ์ด ๋๋ฅผ ๋ฏฟ์ง ์๋๋ค๋ฉด, ๋น์ ์ ๊ทธ๊ฒ์ ํ
์คํธ ํ ์ ์์ต๋๋ค.
๋ฐ๋ผ์ ๊ทํ๊ฐ ์ค๋ช ํ ์๋ ๋ด๊ฐ ๊ฐ์ง ๋ฌธ์ ์ ์ ํฉํ์ง ์์ต๋๋ค. ๋ด๊ฐ ๋ชจ๋ฅด๋ ๊ฒ์ ์๊ณ ์์ต๋๊น?
@cpojer
๋๋ ์ด๊ฒ์ ์์ฃผ ์ ์ดํดํ๊ณ ์๋ค๊ณ ๋ฏฟ์ต๋๋ค. ๊ทธ๋ฌ๋ babel์ด ๋ชจ๋์ ์ปดํ์ผํ๋ ๋ฐฉ์์ ์ด๊ฒ์ ์ดํดํ๊ธฐ ์ฝ๊ฒ ๋ง๋ค์ง ์์ผ๋ฉฐ ๋๋ ๋น์ ์ ํผ๋์ ์ดํดํฉ๋๋ค. ์ ๋ ์ด๊ฒ์ด ๋ชจ๋์ด ์๋ ์ค์ ES2015 ํ๊ฒฝ์์ ์ด๋ป๊ฒ ์๋ํ ์ง ์ ํํ ๋ชจ๋ฆ ๋๋ค. ์ฃผ๋ก ๊ทธ๋ฌํ ํ๊ฒฝ์ด ํ์ฌ ์กด์ฌํ์ง ์๊ธฐ ๋๋ฌธ์ ๋๋ค(์์ง ์๋ํ์ง ์์ ์ต์ ๋ฒ์ ์ Chrome Canary ์ ์ธ). ๋ฌด์จ ์ผ์ด ์ผ์ด๋๋์ง ์ค๋ช ํ๋ ค๋ฉด babel ์ฝ๋์ ์ปดํ์ผ๋ ์ถ๋ ฅ์ ๋ด์ผ ํฉ๋๋ค. ๋ค์๊ณผ ๊ฐ์ด ๋ณด์ผ ๊ฒ์ ๋๋ค.
var foo = function foo() {};
var bar = function bar() { foo(); };
exports.foo = foo;
exports.bar = bar;
์ด ๊ฒฝ์ฐ foo๋ฅผ ์กฐ๋กฑํ ์ ์๋ ๊ฒ์ด ์ฌ์ค์ด๋ฉฐ ์ด๊ธฐ ๋ฌธ์ ๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ฝ์ง ๋ชปํ ์ ์ ๋ํด ์ฌ๊ณผ๋๋ฆฝ๋๋ค. ๊ทธ๋ฌ๋ foo
๊ฐ ํธ์ถ๋ ๋ฐฉ์์ ๋ํ ๊ฐ์ ์ ํ์ง ์์๊ธฐ ๋๋ฌธ์ exports.foo()
๋ผ๊ณ ๊ฐ์ ํ์ต๋๋ค.
๊ทธ๋ฌ๋ ์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝํ๋ฉด:
var foo = function foo() {};
var bar = function bar() { exports.foo(); };
exports.foo = foo;
exports.bar = bar;
๊ทธ๋ฐ ๋ค์ ํ ์คํธ ํ์ผ์์ ๋ค์์ ์ํํฉ๋๋ค.
var module = require('../module');
module.foo = jest.fn();
module.bar();
์์๋๋ก ์๋ํฉ๋๋ค. ์ด๊ฒ์ด ์ฐ๋ฆฌ๊ฐ ES2015๋ฅผ ์ฌ์ฉํ์ง ์๋ Facebook์์ ํ๋ ์ผ์ ๋๋ค.
ES2015 ๋ชจ๋์ ๋ด๋ณด๋ด๋ ํญ๋ชฉ์ ๋ํด ๋ณ๊ฒฝํ ์ ์๋ ๋ฐ์ธ๋ฉ์ ๊ฐ์ง ์ ์์ง๋ง ํ์ฌ babel์ด ์ปดํ์ผํ๋ ๊ธฐ๋ณธ ์ปดํ์ผ๋ ์ฝ๋๋ ์ด๋ฌํ ์ ์ฝ ์กฐ๊ฑด์ ์ ์ฉํ์ง ์์ต๋๋ค. ํ์ฌ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์๋๋ ๋ชจ๋์ด ์๋ ์๊ฒฉํ ES2015 ๋ชจ๋ ํ๊ฒฝ์์ ๊ทํ๊ฐ ์๊ตฌํ๋ ๊ฒ์ ์ ํํ ์ง์ํ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. jest-mock
์๋ํ๋ ๋ฐฉ์์ ๋ชจ๋ ์ฝ๋๋ฅผ ๋ถ๋ฆฌํ์ฌ ์คํํ ๋ค์ ๋ชจ๋์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ํ๊ณ ๋ชจ์ ํจ์๋ฅผ ์์ฑํ๋ ๊ฒ์
๋๋ค. ๋ค์ ๋งํ์ง๋ง, ์ด ๊ฒฝ์ฐ foo
์ ๋ก์ปฌ ๋ฐ์ธ๋ฉ์ ์์ ํ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. ์ด๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๋ํ ์์ด๋์ด๊ฐ ์์ผ๋ฉด ์ฌ๊ธฐ ๋๋ ํ ์์ฒญ์ผ๋ก ๊ธฐ์ฌํ์ญ์์ค. https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct ์์ ์ฝ์ ์ ์๋ ์ด ํ๋ก์ ํธ์ ๋ํ ํ๋ ๊ฐ๋ น์ด ์์์ ์๊ธฐ์ํค๊ณ ์ถ์ต๋๋ค.
์์ ์์ ์ฌ๋ฐ๋ฅธ ์๋ฃจ์ ์ foo๋ฅผ ์กฐ๋กฑํ๋ ๊ฒ์ด ์๋๋ผ foo๊ฐ ํธ์ถํ๋ ์์ ์์ค API(์: XMLHttpRequest ๋๋ ๋์ ์ฌ์ฉํ๋ ์ถ์ํ)๋ฅผ ์กฐ๋กฑํ๋ ๊ฒ์ ๋๋ค.
@cpojer ์์ธํ ์ค๋ช
๊ฐ์ฌํฉ๋๋ค. ๋ด ์ธ์ด๋ก ๊ธฐ๋ถ์ ์ํ๊ฒ ํ๋ค๋ฉด ์ฃ์กํฉ๋๋ค. ์ ๋ ์์ง๋์ด๋ง ์์ฑ์ ๋งค์ฐ ๋ฅ์ํ๋ฉฐ ์ต๋ํ ๋นจ๋ฆฌ ์ ์์ ์ ์ ๋ฌํ๊ณ ์ถ์ต๋๋ค. ์ํฉ์ ์ดํดํ๊ธฐ ์ํด 5์๊ฐ ๋์ ์ด ๋ฌธ์ ๋ฅผ ์ดํดํ๋ ค๊ณ ๋
ธ๋ ฅํ๊ณ 2๊ฐ์ ์์ธํ ์ค๋ช
์ ์์ฑํ์ต๋๋ค. ๊ทธ๋ฐ ๋ค์ ๊ทํ๋ ๋ด ๋ ์ง์ ์ ์์ ์ ์์ ํ ๋์น ์งง์ ๋ฉ์์ง๋ก ๋ง๋ฌด๋ฆฌํ์ต๋๋ค. ๊ทธ๊ฒ์ด ๋ฐ๋ก ๋ค์ ๋ฉ์์ง๊ฐ 1) ๋ด๊ฐ ๋งํ๊ณ ์๋ ์์ ์ ์ดํดํ์ง ๋ชปํ๊ฑฐ๋ 2) require()
์ดํดํ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ "๊ทผ๋ณธ์ ์ธ ์คํด"๊ฐ ์๋ค๊ณ ๋งํ ์ด์ ์
๋๋ค. ๊ณ ๋ง๊ฒ๋ ์ต์
1์ด์์ต๋๋ค.
๋๋ ๋ด ๋ฌธ์ ์ ๋ํ ๊ฐ๋ฅํ ํด๊ฒฐ์ฑ ์ ์๊ณ ํ ๊ฒ์ ๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ง๊ธ์ ๋ฎ์ ์์ค์ API๋ฅผ ์กฐ๋กฑํ์ง๋ง, ํ์คํ ์ ์ฉํ ๊ฒ์ด๋ฏ๋ก ํจ์๋ฅผ ์ง์ ์กฐ๋กฑํ๋ ๋ฐฉ๋ฒ์ด ์์ด์ผ ํฉ๋๋ค.
์ด ์์ ์ ์ํํ๋ ๊ฒ์ด ์ ์ฉํ ๊ฒ์ด๋ผ๋ ๋ฐ ๋์ํ์ง๋ง JS์์ (์๋ง๋ ๋๋ฆฐ) ์ ์ ๋ถ์์ ์ฌ์ ์ ์ํํ์ง ์๊ณ ์ํํ๋ ์ข์ ๋ฐฉ๋ฒ์ ์์ต๋๋ค.
@cpojer : 5๊ฐ์ ํ์ ์ฌ๊ธฐ๋ก ๋ฐ์ด๋๋ ๊ฒ์ด ๊ฐ ๊ธธ์ธ์ง ํ์ ํ ์ ์์ง๋ง ์ด์ ๋ํ ๋ค๋ฅธ ๋ํ๋ ์ฐพ์ ์ ์์์ต๋๋ค.
์์ ์ ์์์ ๋ฒ์ด๋ ๋์ผํ ๋ชจ๋์ ๋ค๋ฅธ ๊ธฐ๋ฅ์์ ํ ๊ธฐ๋ฅ์ ์กฐ๋กฑํ๊ธฐ ์ํด ์ด ์์ ์ ์ํํ์ต๋๋ค.
jest.unmock('./someModule.js');
import someModule from './someModule.js';
it('function1 calls function 2', () => {
someModule.function2 = jest.fn();
someModule.function1(...);
expect(someModule.function2).toHaveBeenCalledWith(...);
});
์ด๊ฒ์ ํ๋์ ํ
์คํธ์์ ์๋ํ์ง๋ง ํ๋์ it(...);
๋ธ๋ก์ผ๋ก ๋ถ๋ฆฌ๋ ๋ฐฉ์์ผ๋ก ์ด๋ฅผ ์ํํ๋ ๋ฐฉ๋ฒ์ ์ฐพ์ง ๋ชปํ์ต๋๋ค. ์์์ ์ค๋ช
ํ ๊ฒ์ฒ๋ผ ๋ชจ๋ ํ
์คํธ์ ์ํฅ์ ๋ฏธ์น๋ฏ๋ก ๋ค๋ฅธ ํ
์คํธ์์ ์ค์ function2
๋ฅผ ํ
์คํธํ๊ธฐ ์ด๋ ต์ต๋๋ค. ํ์ด ์๋์?
๋น์ ์ ํธ์ถ ํ ์ ์์ต๋๋ค .mockClear
์์ ํจ์์ beforeEach
๋๋ ์ ํ jest.clearAllMocks()
๋น์ ์ด ๋๋ด (16) ์ฌ์ฉํ๋ ๊ฒฝ์ฐ.
์๋
ํ์ธ์ @cpojer์
๋๋ค! ์ ๋ Jest 16์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. jest.clearAllMocks()
๋๋ someModule.function2.mockClear()
๋ ๋ค ์ ์๊ฒ ์ ํฉํ์ง ์์ต๋๋ค. ๊ทธ๋ค์ ๋ชจ์๊ฐ ๊ฐ์ ธ์จ ๋ชจ๋์ ๊ธฐ๋ฅ์ด ์๋ ์ ์ฒด ๋ชจ๋์ผ ๋๋ง ์๋ํฉ๋๋ค. ๋ด ํ๋ก์ ํธ์์ ํจ์๋ ํ์ ํ
์คํธ์์ ๋ชจ์ โโ์ํ๋ก ์ ์ง๋ฉ๋๋ค. ์ด๊ฒ์ด ์์๋์ง ์์ ๊ฒฝ์ฐ ์์ ์ํ ํ๋ก์ ํธ์์ ๋ณต์ ํ๊ณ ์ ๋ฌธ์ ๋ฅผ ๋ง๋ค ์ ์๋์ง ํ์ธํ๊ฒ ์ต๋๋ค. ์ข์ ์๊ฐ?
@cpojer -
์์ ์์ ์ฌ๋ฐ๋ฅธ ์๋ฃจ์ ์ foo๋ฅผ ์กฐ๋กฑํ๋ ๊ฒ์ด ์๋๋ผ foo๊ฐ ํธ์ถํ๋ ์์ ์์ค API(์: XMLHttpRequest ๋๋ ๋์ ์ฌ์ฉํ๋ ์ถ์ํ)๋ฅผ ์กฐ๋กฑํ๋ ๊ฒ์ ๋๋ค.
์ ๋ Jest๋ฅผ ์ฒ์ ์ฌ์ฉํ๊ณ ๋น์ทํ ๋ฌธ์ ๋ก ์ด๋ ค์์ ๊ฒช๊ณ ์์ต๋๋ค. ๋๋ ํ๋ ์๋์์ XMLHttpRequest
์ฌ์ฉํ๋ axios ๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ฉฐ axios
๋ฅผ ์กฐ๋กฑํ๊ณ ์ถ์ง ์์ง๋ง ์ค์ XMLHttpRequest
๋ฅผ ์กฐ๋กฑํ๊ณ ์ถ์ต๋๋ค. ๋ด๊ฐ ์์ ์ ์ํด ๊ฐ์ ๋ฉ์๋๋ฅผ ๊ตฌํํด์ผ ํ ๊ฒ ๊ฐ์ต๋๋ค ์ด . ์ด๊ฒ์ด ์ฌ๋ฐ๋ฅธ ์ ๊ทผ ๋ฐฉ์์
๋๊น?
๊ฐ์ฌ ํด์!
๋ค, ์ด์ ๊ฐ์ ๊ฒ์ด ๋น์ ์ ์ฌ๋ฐ๋ฅธ ๊ธธ๋ก ์ธ๋ํ ๊ฒ์
๋๋ค! :) jest.fn
๋ฅผ ๋ ๋ฉ์ง API๋ก ์ฌ์ฉํ์ธ์. :D
์ฌ๊ธฐ์์ ๊ทํ์ ์๊ฒฌ์ ๊ดํ @cpojer : https://github.com/facebook/jest/issues/936#issuecomment -214939935
ES2015๋ก ์ด๋ป๊ฒ ํ์๊ฒ ์ต๋๊น?
// myModyle.js
export foo = (string) => "foo-" + string
export bar = (string2) => foo(string2)
// myModule.test.js
var module = require('./myModule');
// how do I mock foo here so this test passes?
expect(bar("hello")).toEqual("test-hello")
์๋ฃจ์ ์ ์ฐพ๊ณ ์๋ ์ด ๋ฌธ์ ๋ฅผ ์ ํ๋ ์ฌ๋์๊ฒ๋ ํ๋์ ํ์ผ์์ ๋ง์ const/function์ ๋ด๋ณด๋ด๊ณ ํ ์คํธ ์ค์ธ ํ์ผ๋ก ๊ฐ์ ธ์ฌ ๋ ๋ค์์ด ์ ์๊ฒ ๋์์ด ๋๋ ๊ฒ ๊ฐ์ต๋๋ค.
`` javascript
function mockFunctions() {
const original = require.requireActual('../myModule');
return {
...original, //Pass down all the exported objects
test: jest.fn(() => {console.log('I didnt call the original')}),
someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}),
}
jest.mock('../myModule', () => mockFunctions());
const storage = require.requireMock('../myModule');
@ainesophaur , ๋ด๊ฐ ์ฌ๊ธฐ์ ๋ญ ์๋ชปํ๊ณ ์๋์ง ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๊ฒ์ ์๋ํ์ง ์๋ ๊ฒ ๊ฐ์ต๋๋ค
์ ๋ ํ์ฌ jest 18.1(๋ฐ create-react-app 0.9.4)์ ์ฌ์ฉ ์ค์
๋๋ค.
...<codes from comment above>..
// Let's say the original myModule has a function doSmth() that calls test()
storage.doSmth();
expect(storage.test).toHaveBeenCalled();
ํ ์คํธ๋ ๋ค์๊ณผ ํจ๊ป ์คํจํฉ๋๋ค.
expect(jest.fn()).toHaveBeenCalled()
Expected mock function to have been called.
@huyph jest ๊ฐ ํธ์ถ๋์๋์ง ์ฌ๋ถ๋ฅผ ํ ์คํธํ๋ ค๋ฉด doSmth ๋ฉ์๋์ ํ ์คํธ ๋ฉ์๋๋ฅผ ์กฐ๋กฑํด์ผ ํฉ๋๋ค. ์กฐ๋กฑ ์ฝ๋์ ์ค๋ํซ์ ์ ๊ณตํ ์ ์๋ค๋ฉด ๋ฌด์์ด ์๋ชป๋์๋์ง ํ์ธํ ์ ์์ต๋๋ค.
@ainesophaur ... test()
๋ฉ์๋๋ฅผ ์กฐ๋กฑํ๊ธฐ ์ํ ๊ฒ์
๋๊น? ์ด ๋ถ๋ถ: test: jest.fn(() => {console.log('I didnt call the original')}),
@ainesophaur ๊ทํ์ ์ฝ๋๋ ์๋ํ์ต๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๊ฒ์ ๋๋ฅผ ์ํด ์๋ํ์ง ์์์ต๋๋ค. ๋ชจ์ ํจ์๋ฅผ ์คํํ์ง ์์ต๋๋ค. ๋ฐ๋ผ์ ๊ธฐ๋๋ ๊ฒฐ์ฝ ์ถฉ์กฑ๋์ง ์์ต๋๋ค.
๋๋ ์ด๊ฒ์ด ์์์ ์ธ๊ธํ ๊ฒ๊ณผ ๊ฐ์ ์์ ์ ์๊ตฌํ๋ ๋ฐฉ์์ ๋ด์ฌ๋์ด ์๋ค๊ณ ์๊ฐํฉ๋๋ค. ์ด์ ๋ํ ํด๊ฒฐ์ฑ ์ด ์์์ผ๋ฉด ํฉ๋๋ค.
@cpojer ๋ถ๋ถ์ ์ผ๋ก ๋ชจ์ ๋ชจ๋๊ณผ ๊ด๋ จํ์ฌ ์๋ก์ด ๊ฒ์ด ์์ต๋๊น?
@rantonmattei & @huyph ๋ชจ์ ์ ์ ์ค๋ํซ๊ณผ ์คํ ์ค์ธ ํ ์คํธ๋ฅผ ํ์ธํด์ผ ํฉ๋๋ค. ์ค์ ๊ตฌํ ํ์ผ์ด ํ์/๊ฐ์ ธ์ค๊ธฐ ์ ์ ๋ชจ์๋ฅผ ์ ์ํด์ผ ํฉ๋๋ค. JEST๋ก ์์ ํ ์ง ๊ฝค ๋์์ง๋ง, node_modules ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ด ์ฑ์ ํ์ผ์ด๋ ๊ฐ์ ๋ด๊ฐ ํ์ํ ๋ชจ๋ ๊ฒ์ ๋ชจ์ํ๊ธฐ ์ํด ๊ฒฐ๊ตญ JEST๋ฅผ ์ป์์์ ๊ธฐ์ตํฉ๋๋ค. ์ ๋ ATM ์๊ฐ์ด ์กฐ๊ธ ๋ถ์กฑํ์ง๋ง Jest๋ฅผ ์ฌ์ฉํ์ฌ ์์ ํ ํ๋ก์ ํธ์ ๋ช ๊ฐ์ง ํ ์คํธ๊ฐ ์์ต๋๋ค.
์ข ์์ฑ์์ ํ์ผ ์กฐ๋กฑ
์ด ์์ ์์ ์ค์ ํจ์ ์ ์๋ react-native์ ์ํด ์ํ๋ฉ๋๋ค. "react-native/Libraries/Utilities/dismissKeyboard.js" ํ์ผ์ ์กฐ๋กฑํ๊ณ ์์ต๋๋ค.
__mocks__/react-native/Libraries/Utilities/dismissKeyboard.js
์๋์ ๋ชจ์ ํ์ผ์
๋๋ค.
function storeMockFunctions() {
return jest.fn().mockImplementation(() => true);
}
jest.mock('react-native/Libraries/Utilities/dismissKeyboard', () => storeMockFunctions(), { virtual: true });
const dismissKeyboard = require('react-native/Libraries/Utilities/dismissKeyboard');
exports = module.exports = storeMockFunctions;
์์์ ์ฌ์ฉํ ํ ์คํธ ํ์ผ์ ์ฐพ์ ์ ์์ง๋ง ๋ชจ๋์ ์๊ตฌํ๋ ๊ฒ๊ณผ ๊ฐ์์ต๋๋ค. jest๊ฐ __mocks__์์ ์ฐพ์ ๋ค์ ๋ค์๊ณผ ๊ฐ์ด ํ ์ ์์ต๋๋ค.
expect(dismissKeyboard.mock.calls).toHaveLength(1);
์ ์ดํ๋ ํ์ผ ์กฐ๋กฑ
์ค์ ํจ์ ์ ์
export const setMerchantStores = (stores) => storage.set('stores', stores);
๋ชจ์ ํ ์คํธ ํ์ผ
const { storeListEpic, offerListEpic } = require('../merchant');
function storeMockFunctions() {
const original = require.requireActual('../../common/Storage');
return {
...original,
setMerchantStores: jest.fn((...args) => original.setMerchantStores(...args)),
setMerchantOffers: jest.fn((...args) => original.setMerchantOffers(...args)),
};
}
jest.mock('../../common/Storage', () => storeMockFunctions());
import * as storage from '../../common/Storage';
afterEach(() => {
storage.setMerchantStores.mockClear();
});
it('handle storeListEpic type STORE_LIST_REQUEST -> STORE_LIST_SUCCESS', async () => {
const scope = nock('http://url')
.get('/api/merchant/me/stores')
.reply(200, storeData);
const result = await storeListEpic(ActionsObservable.of(listStores())).toPromise();
expect(storage.setMerchantStores.mock.calls).toHaveLength(1);
expect(await storage.getMerchantStores()).toEqual({ ids: storesApiData.result, list: storesApiData.entities.store});
});
@ainesophaur๋ฅผ ๊ณต์ ํด ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. ๋๋ ์ฌ์ ํ jest 18.1์์ ์๋ํ๋๋ก ํ ์ ์์ต๋๋ค. ๋ด ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
it('should save session correctly', () => {
function mockFunctions() {
const original = require.requireActual('./session');
return {
...original,
restartCheckExpiryDateTimeout: jest.fn((() => {
console.log('I didn\'t call the original');
})),
}
}
jest.mock('./session', () => mockFunctions());
const mockSession = require('./session');
// NOTE: saveSession() will call the original restartCheckExpiryDateTimeout() instead of my
// mock one. However, mockSession.restartCheckExpiryDateTimeout() does call the mock one
mockSession.saveSession('', getTomorrowDate(), 'AUTH');
// mockSession.restartCheckExpiryDateTimeout(); // does print out "I didn't call the original"
expect(mockSession.restartCheckExpiryDateTimeout).toHaveBeenCalled();
});
session.js
export function saveSession(sessionId, sessionExpiryDate, authToken) {
....
restartCheckExpiryDateTimeout(sessionExpiryDate);
...
}
....
export function restartCheckExpiryDateTimeout(...) {
....
}
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ๋ฐฉ๋ฒ์ ์ฐพ์ ์ ์์ต๋๋ค. ์ด๊ฑฐ ๋ค์ ์ด์ด๋ ๋ ๊น์? @cpojer
@huyph saveSession
๋ด๋ณด๋ด๊ธฐ๋ฅผ ์ํํ๋ ๋ฐฉ์์ ๋ชจ๋์ ํตํด module.restartCheckExpiryDateTimeout
ํธ์ถํ๋ ๋์ ๋ก์ปฌ๋ก ์ ์๋ restartCheckExpiryDateTimeout
๋ฅผ ํธ์ถํ๋ ๊ฒ์ด๋ฏ๋ก ์กฐ๋กฑ๋ module.restartCheckExpiryDateTimeout
saveSession
๋ ์ค์ ์ ์๋ restartCheckExpiryDateTimeout
ํจ์๋ฅผ ํธ์ถํ๋ฏ๋ก module.restartCheckExpiryDateTimeout
๋ saveSession
์ํด ๊ฐ์ง๋์ง ์์ต๋๋ค.
saveSession
๋ฅผ const ์ ํ ๋นํ ๋ค์ saveSession.restartCheckExpiryDateTimeout = () => {...logic}
ํฉ๋๋ค. ๊ทธ ๋๋ ๋ด์์ saveSession.saveSession
์ ํ saveSession.restartCheckExpiryDateTimeout
๋์ ์ restartCheckExpiryDateTimeout
. ๋ฉ์๋๋ฅผ ์ ์ํ๋ ์ค์ ํจ์ saveSession
๋์ ์ const๋ฅผ ๋ด๋ณด๋
๋๋ค. ๊ทธ๋ฐ ๋ค์ someVar.saveSession()
ํธ์ถํ๋ฉด ๋ด๋ถ์ ์ผ๋ก saveSession.restartCheckExpiryDateTimeout()
๊ฐ ํธ์ถ๋๋ฉฐ ํ์ฌ ์กฐ๋กฑ๋ฉ๋๋ค.
restartCheckExpiryDateTimeout()
๊ฐ ๋ด๋ณด๋ธ ํจ์๋ผ๊ณ ์ถ๊ฐํ์ด์ผ ํฉ๋๋ค. saveSession()
๋ด์์ ๋ก์ปฌ๋ก ์ ์๋ ํจ์๊ฐ ์๋๋๋ค ... (์์ ๋ด ์๊ฒฌ์ ์
๋ฐ์ดํธํจ). ์ด ๊ฒฝ์ฐ module.saveSession()
๋ ์กฐ๋กฑ๋๋ ์ฌ๋ฐ๋ฅธ module.restartCheckExpiryDateTimeout()
๋ฅผ ํธ์ถํด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๊ทธ๋ฌ๋ ๋๋ ๋น์ ์ด ์์์ ์ ์ํ ๊ฒ์ ์ค ๊ฒ์
๋๋ค. saveSession()
๋ฐ restartCheckExpiryDateTimeout()
๋ฅผ ๋ชจ๋ ๋ค๋ฅธ const๋ก ์ด๋ํฉ๋๋ค. ๊ฐ์ฌ ํด์
๋๋ ๊ทธ๊ฒ์ด saveSession์ ๋ฒ์์ ์ ์๋์ด ์์ง ์๋ค๋ ๊ฒ์ ์ดํดํฉ๋๋ค. ์ธ์ด๋ธ์ธ์
์
๋ถ๋ชจ ๋ฒ์์์ ํ์ ๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค. ๋๋ ์ด๊ฒ์ ์ฌ๋ฌ ๋ฒ ๋ถ๋ช์ณค๋ค.
๊ทธ๋ฆฌ๊ณ ๋ด๊ฐ ์ ์ํ ๊ฒ์ ํจ๊ณผ๊ฐ ์์๋ค
2017๋ 5์ 8์ผ ์คํ 8์ 38๋ถ์ "Huy Pham" [email protected]์ด ์์ฑํ์ต๋๋ค.
๋๋ restartCheckExpiryDateTimeout()์ด ๋ด๋ณด๋ด์ก๋ค๊ณ ์ถ๊ฐํ์ด์ผ ํ๋ค.
๊ธฐ๋ฅ. saveSession() ๋ด์์ ๋ก์ปฌ๋ก ์ ์๋ ํจ์๊ฐ ์๋๋๋ค...๋๋ ๋น์ ์ด ์์์ ์ ์ํ ๊ฒ์ ์ค ๊ฒ์ ๋๋ค. ๊ฐ์ฌ ํด์
โ
๋น์ ์ด ์ธ๊ธ๋์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ฐ๋ ๊ฒ์ ๋๋ค.
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ณ GitHub์์ ํ์ธ
https://github.com/facebook/jest/issues/936#issuecomment-300029003 ๋๋ ์์๊ฑฐ
์ค๋ ๋
https://github.com/notifications/unsubscribe-auth/AEeBdsmpOOmzvcUHB3D_-Z7MChIzt10Pks5r37WYgaJpZM4IPGAH
.
๋ฐฉ๊ธ ์๋ํ์ต๋๋ค. ๋๋ ๊ทธ๊ฒ์ ๋ฐ๊ฒฌํ์ต๋๋ค.
์ด๊ฒ์ ์๋ํ์ง ์์ต๋๋ค: (์ฆ, ์๋์ restartCheckExpiryDateTimeout()์ด ์ฌ์ ํ ํธ์ถ๋จ)
export session = {
saveSession: () => {
session.restartCheckExpiryDateTimeout();
},
restartCheckExpiryDateTimeout: () => {},
}
์ด๊ฒ์ ์๋ํฉ๋๋ค(์ฆ, ๋ชจ์ restartCheckExpiryDateTimeout()๊ฐ ๋์ ํธ์ถ๋จ). ์ฐจ์ด๋ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค function()
๋์ ํ์ดํ ํํ ๋ฐ ์ฌ์ฉ this.
๋์ session.
export session = {
saveSession: function() {
this.restartCheckExpiryDateTimeout();
},
restartCheckExpiryDateTimeout: () => {},
}
์ด ์ฝ๋๋ฅผ ๋๋ด์ผ๋ก ๋ณํํ๋ ๊ฒ์ ๋ฌธ์ ๊ฐ ๋ ์ ์์ต๋๋ค ....
pojo ๋์ ํด๋์ค ๊ฐ์ฒด๋ก ๋ด๋ณด๋ด๋ณด์ญ์์ค. ๋๋ ๋ฏฟ๋๋ค
ํธ๋์คํ์ผ๋ฌ๋ ๋ณ์๋ฅผ ๋ค๋ฅด๊ฒ ํธ์ด์คํธํฉ๋๋ค. ์ฐ๋ฆฌ๋ ์์
์ ๋์ฐฉํ ๊ฒ์
๋๋ค
ํ
์คํธ, ์ฝ์ํฉ๋๋ค. ํ๋ก์ ํธ๋ฅผ ์์ํ์ง โโ๋ฐ๋
์ ๋ ๋์์ต๋๋ค.
๋๋ด์ ์ฌ์ฉํ์ง๋ง์ด ๋ฌธ์ ๋ฅผ ์ ๊ธฐ์ตํ๊ณ ๊ฒฐ๊ตญ ๊ธฐ์ตํฉ๋๋ค.
ํด๊ฒฐ์ฑ
์ ์ฐพ๋ ๊ฒ.
2017๋ 5์ 9์ผ ์ค์ 12์ 53๋ถ์ "Huy Pham" [email protected]์ด ์์ฑํ์ต๋๋ค.
๋ฐฉ๊ธ ์๋ํ์ต๋๋ค. ๋๋ ๊ทธ๊ฒ์ ๋ฐ๊ฒฌํ์ต๋๋ค.
์ด๊ฒ์ ์๋ํ์ง ์์ต๋๋ค: (์ฆ, ์๋์ restartCheckExpiryDateTimeout()์
์ฌ์ ํ ํธ์ถ๋จ)๋ด๋ณด๋ด๊ธฐ ์ธ์ = {
์ธ์ด๋ธ์ธ์ : () => {
session.restartCheckExpiryDateTimeout();
},
restartCheckExpiryDateTimeout: () => {},
}์ด๊ฒ์ ์๋ํ์ง ์์ต๋๋ค: (์ฆ, ์๋์ restartCheckExpiryDateTimeout()์
์ฌ์ ํ ํธ์ถ๋จ)๋ด๋ณด๋ด๊ธฐ ์ธ์ = {
์ธ์ด๋ธ์ธ์ : ํจ์() {
this.restartCheckExpiryDateTimeout();
},
restartCheckExpiryDateTimeout: () => {},
}์ด ์ฝ๋๋ฅผ ๋๋ด์ผ๋ก ๋ณํํ๋ ๊ฒ์ ๋ฌธ์ ๊ฐ ๋ ์ ์์ต๋๋ค ....
โ
๋น์ ์ด ์ธ๊ธ๋์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ฐ๋ ๊ฒ์ ๋๋ค.
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ณ GitHub์์ ํ์ธ
https://github.com/facebook/jest/issues/936#issuecomment-300060975 ๋๋ ์์๊ฑฐ
์ค๋ ๋
https://github.com/notifications/unsubscribe-auth/AEeBdrRQExycPYiGtvm7qYi5G87w6b6Oks5r3_FlgaJpZM4IPGAH
.
@sorahn ๊ฐ์ ๋ฌธ์ ์
๋๋ค. es6
+ babel
, ์กฐ๋กฑํ๋ ๋ฐฉ๋ฒ?
@cpojer es6
+ babel
, export const function xx() {}
, export many function , Jest
ํธ์ถ๋ ๋ชจ๋(ํ์ผ)์์ ํจ์๋ฅผ ์กฐ๋กฑํ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. ๊ฐ์ ๋ชจ๋(ํ์ผ)์ ๋ค๋ฅธ ๊ธฐ๋ฅ์ผ๋ก? ํ
์คํธํด๋ณด๋ ๋ง๋ ๊ฒ ๊ฐ์ต๋๋ค. commonjs
ํจํด์ ๋ํด์๋ง Jest๋ ๊ทํ์ ์์ ๊ฐ์ด ํจ์๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ๋ชจ์ํ ์ ์์ต๋๋ค.
@ainesophaur ๊ฐ ์๋ํ์ง ์์ต๋๋ค.
๊ธฐ์ค ์น์:
export const getMessage = (num: number): string => {
return `Her name is ${genName(num)}`;
};
export function genName(num: number): string {
return 'novaline';
}
์ํ:
function mockFunctions() {
const original = require.requireActual('../moduleA');
return {
...original,
genName: jest.fn(() => 'emilie')
}
}
jest.mock('../moduleA', () => mockFunctions());
const moduleA = require('../moduleA');
describe('mock function', () => {
it('t-0', () => {
expect(jest.isMockFunction(moduleA.genName)).toBeTruthy();
})
it('t-1', () => {
expect(moduleA.genName(1)).toBe('emilie');
expect(moduleA.genName).toHaveBeenCalled();
expect(moduleA.genName.mock.calls.length).toBe(1);
expect(moduleA.getMessage(1)).toBe('Her name is emilie');
expect(moduleA.genName.mock.calls.length).toBe(2);
});
});
๊ฒ์ฌ ๊ฒฐ๊ณผ:
FAIL jest-examples/__test__/mock-function-0.spec.ts
โ mock function โบ t-1
expect(received).toBe(expected)
Expected value to be (using ===):
"Her name is emilie"
Received:
"Her name is novaline"
at Object.it (jest-examples/__test__/mock-function-0.spec.ts:22:35)
at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)
mock function
โ t-0 (1ms)
โ t-1 (22ms)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 0.215s, estimated 1s
์์ ๋ง์ง๋ง ๋ช ๊ฐ์ง ์๊ฒฌ์ ๋ณด์ญ์์ค. ํนํ ๋ง์ง๋ง. ๋น์ ์
๋ด๋ณด๋ธ ๋ฉ์๋๋ ๋ก์ปฌ ๋ฒ์์ ํ์ ๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ
์ค์ ๋ด๋ณด๋ธ ๋ฉ์๋(๋ชจ์ ๊ฐ์ฒด๊ฐ ์๋ ๊ณณ)
2017๋ 5์ 31์ผ ์ค์ 2์ "novaline" [email protected]์์ ๋ค์๊ณผ ๊ฐ์ด ์ผ์ต๋๋ค.
@ainesophaur https://github.com/ainesophaur ๊ฐ ์๋ํ์ง ์์ต๋๋ค.
๊ธฐ์ค ์น์:
๋ด๋ณด๋ด๊ธฐ const getMessage = (์ซ์: ์ซ์): ๋ฌธ์์ด => {
๋ฐํHer name is ${genName(num)}
;
};
๋ด๋ณด๋ด๊ธฐ ํจ์ genName(num: number): string {
๋ฐํ '๋ ธ๋ฐ๋ฆฐ';
}์ํ:
ํจ์ mockFunctions() {
const ์๋ณธ = require.requireActual('../moduleA');
๋ฐํ {
...์๋์,
genName: jest.fn(() => '์๋ฐ๋ฆฌ')
}
}jest.mock('../moduleA', () => mockFunctions()); const moduleA = require('../moduleA');
describe('๋ชจ์ ํจ์', () => {it('t-0', () => {
๊ธฐ๋(jest.isMockFunction(moduleA.genName)).toBeTruthy();
})it('t-1', () => {
expect(moduleA.genName(1)).toBe('emilie'); expect(moduleA.genName).toHaveBeenCalled(); expect(moduleA.genName.mock.calls.length).toBe(1); expect(moduleA.getMessage(1)).toBe('Her name is emilie'); expect(moduleA.genName.mock.calls.length).toBe(2);
});
});
๊ฒ์ฌ ๊ฒฐ๊ณผ:
FAIL jest-examples/__test__/mock-function-0.spec.ts
โ ๋ชจ์ ๊ธฐ๋ฅ โบ t-1expect(received).toBe(expected) Expected value to be (using ===): "Her name is emilie" Received: "Her name is novaline" at Object.it (jest-examples/__test__/mock-function-0.spec.ts:22:35) at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)
๋ชจ์ ํจ์
โ t-0(1ms)
โ t-1(22ms)ํ ์คํธ ์ค์ํธ: 1๊ฐ ์คํจ, ์ด 1๊ฐ
ํ ์คํธ: 1๊ฐ ์คํจ, 1๊ฐ ํต๊ณผ, ์ด 2๊ฐ
์ค๋ ์ท: ์ด 0๊ฐ
์๊ฐ: 0.215์ด, ์์ 1์ดโ
๋น์ ์ด ์ธ๊ธ๋์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ฐ๋ ๊ฒ์ ๋๋ค.
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ณ GitHub์์ ํ์ธ
https://github.com/facebook/jest/issues/936#issuecomment-305091749 ๋๋ ์์๊ฑฐ
์ค๋ ๋
https://github.com/notifications/unsubscribe-auth/AEeBdv6SafXlTtKo3DNeFWhbL6gV9l0Gks5r_QHjgaJpZM4IPGAH
.
@ainesophaur : export class Session { }
์๋ํ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ๊ฒ์ ๋๋ฅผ ์ํด ์๋ํ์ง ์์ต๋๋ค.
์ฌ๊ธฐ์ ์ ์ ๋ด ๋๊ธ์์ํ์ ํด๋น ์ํ์ ์ ๊ทผ function
๊ตฌ๋ฌธ ๋์ ํ์ดํ์ ์ฌ์ฉ () =>
. ์ฌ๊ธฐ:
export const session = {
saveSession: function() {
this.restartCheckExpiryDateTimeout();
},
restartCheckExpiryDateTimeout: () => {},
}
์ด๊ฒ์ Jest 20.0.3์ ์์ต๋๋ค.
๋ด๊ฐ ํ๋ ์ผ์ ํจ์์ ๋ํ const ๋ํผ๋ฅผ ๋ง๋ ๋ค์ ํด๋น ๋ํผ๋ฅผ ๋ด๋ณด๋ด๋ ๊ฒ์ ๋๋ค(์: const fns ๋ด๋ณด๋ด๊ธฐ). ๊ทธ๋ฐ ๋ค์ ๋ชจ๋ ๋ด๋ถ์์ fns.functionName์ ์ฌ์ฉํ๊ณ fns.functionName ํจ์๋ฅผ jest.fn()ํ ์ ์์ต๋๋ค.
typescript๋ก ์์ฑ๋ ์ฌ์ฉ์ ์ ์ ๋ชจ๋์ mock ํจ์๋ฅผ ์์ฑํ ๋ ๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ๊ฐ ํธ์ถํ ๋ mock ํจ์๋ ํด๋น ํจ์์ mocked ๋ฒ์ ์ ํธ์ถํ๊ธฐ ๋๋ฌธ์ Coverage ๋ณด๊ณ ์์์ ๋ค๋ฃจ๋ ์๋ ํจ์์ ๋๋ค.
ํ
์คํธ์์ ์๋ ๊ฐ์ ธ์จ 2๊ฐ์ ๊ธฐ๋ฅ์ด ์์ต๋๋ค.
import { getCurrentDate, getStartEndDatesForTimeFrame } from ./../_helpers/date';
๋ณด์๋ค์ํผ getStartEndDatesForTimeFrame
๋ getCurrentDate
์ ์ข
์๋ฉ๋๋ค. ๋ค์ ์ค์ ์ ์ฌ์ฉํ๋ฉด getCurrentDate
ํ
์คํธ๊ฐ ์ ์๋ํ๊ณ ๋ชจ์ ๋ฒ์ ์ ์ฌ์ฉํฉ๋๋ค. ๋ฐ๋ฉด์ ์ด๋ค ์ด์ ๋ก getStartEndDatesForTimeFrame
ํ
์คํธ๋ ์กฐ๋กฑ๋ getCurrentDate
์ฌ์ฉํ์ง ์์ง๋ง ์๋ ๊ตฌํ์ ์ฌ์ฉํ๋ฏ๋ก ํ
์คํธ๊ฐ ์คํจํฉ๋๋ค. Date.now = jest.fn(() => "2017-11-16T20:33:09.071Z");
์ ๊ฐ์ ๋ค์ํ ์ค์ ์ ์๋ํ์ง๋ง ์๋ํ์ง ์์์ต๋๋ค. ์์ด๋์ด๊ฐ ์์ต๋๊น?
export const getCurrentDate = () => new Date();
export const getStartEndDatesForTimeFrame = (timeFrame) => {
...
const todayDate = getCurrentDate();
...
switch (timeframe) {
case TimeFrames.TODAY:
console.log(todayDate); // this always prints the real value in tests instead of the mocked one
start = new Date(todayDate.getFullYear(), todayDate.getMonth(), todayDate.getDate(), 0, 0, 0);
end = new Date(
todayDate.getFullYear(),
todayDate.getMonth(),
todayDate.getDate(), 23, 59, 59,
);
break;
...
return { start: start.toISOString(), end: end.toISOString() }
};
function mockFunctions() {
const original = require.requireActual('../../_helpers/date');
return {
...original,
getCurrentDate: jest.fn(() => '2017-11-16T20:33:09.071Z'),
}
}
jest.mock('../../_helpers/date', () => mockFunctions());
const dateModule = require.requireMock('../../_helpers/date');
describe('getCurrentDate', () => {
it('returns the mocked date', () => {
expect(dateModule.getCurrentDate()).
toBe('2017-11-16T20:33:09.071Z'); // this works well and returns the mocked value
});
});
describe('getStartEndDatesForTimeFrame', () => {
it('returns the start and end dates for today', () => {
expect(dateModule.getStartEndDatesForTimeFrame('today')).toEqual(
{ 'start': '2017-11-15T23:00:00.000Z', 'end': '2017-11-16T22:59:59.000Z' }
); // this one uses the original getCurrentDate instead of the mocked one :(
});
});
๋ฐ๋ผ์ getStartEndDatesForTimeFrame
๋ ์กฐ๋กฑ๋ ์๊ฐ์ด ์๋ ํ์ฌ ์๊ฐ์ ์ฌ์ฉํ๋ฏ๋ก ์คํจํฉ๋๋ค.
@ainesophaur ์ ์ ์์
// imageModel.js
const model = {
checkIfImageExists,
getImageUrl,
generateImagePreview
}
export default model
async function checkIfImageExists(...) {}
async function getImageUrl() {}
async function generateImagePreview() {
// I am calling it as `model` object's method here, not as a locally scoped function
return model.getImageUrl(...)
}
// imageModel.test.js
import imageModel from './imageModel'
test('should mock getImageUrl called within the same file', async () => {
imageModel.getImageUrl = jest.fn().mockReturnValueOnce(Promise.resolve())
await imageModel.generateImagePreview()
expect(imageModel.getImageUrl).toBeCalled()
})
@miluoshi ์ ๋ ํ ์ ์์๋ ์ ์ผํ ๋ฐฉ๋ฒ์ ๋๋ค. ์ด ๋ฐฉ๋ฒ์ ์ฌ์ฉํ ๋ ์ฑ๋ฅ์ด ์ ํ๋๊ฑฐ๋ ์ด์ ์ ์ฌํ ๊ฒ์ด ์์ต๋๊น? ํ ์คํธํ ์ ์๋๋ก ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒ์ "์๋ชป๋" ๊ฒ ๊ฐ์ต๋๋ค.
๋ค์๊ณผ ๊ฐ์ด ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์ ๋ง ์ํฉ๋๋ค.
jest.mock('src/folder/file.func, () => {return 'whatever i want'})
์ฌ๊ธฐ์ ์ค์ํ ๋ถ๋ถ์ .func
@miluoshi @Rdlenke ์ฝ๋๊ฐ ๋ช
๋ช
๋ ๋ด๋ณด๋ด๊ธฐ๋ก ๊ตฌ์ฑ๋์ด ์์ผ๋ฉด import * as model
๋ฅผ ํ ๋ค์ model.generateImagePreview = jest.fn(() => Promise.resolve);
๋ฅผ ๋ฎ์ด์ธ ์๋ ์์ต๋๋ค.
๊ทธ๊ฒ์ sinon์ผ๋ก ์ด๋ป๊ฒ ํ
์คํธํ์๊ฒ ์ต๋๊น? ์์์ ์ธ๊ธํ๋ฏ์ด(https://github.com/facebook/jest/issues/936#issuecomment-214939935 ์ฐธ์กฐ) ESM ์๋ ๋ฐฉ์์ func2
๋ด์์ func1
func2
๋ฅผ ์กฐ๋กฑํ๋ ๊ฒ์ ๋ถ๊ฐ๋ฅํ๊ฒ ๋ง๋ญ๋๋ค. ๋๋ ๊ทธ๊ฒ์ ๋ฐ๋์ ๊ธฐ๋ณธ์ด๋ผ๊ณ ๋ถ๋ฅด์ง ์์ ๊ฒ์ด๋ค.
"testImport" ํจ์์์ ์ฝ๋ babel ๋ชจ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
ํ๊ธฐ ์ ์ ๋ชจ๋์ ๊ธฐ๋ฅ์ ๋ด๋ณด๋ด๋๋ก ์ฝ๋๋ฅผ ๋ค์ ์์ฑํฉ๋๋ค.
ํ
์คํธ ์คํ?
2017๋ 12์ 18์ผ ์์์ผ ์คํ 5์์ Jim Moody [email protected]์ด ๋ค์๊ณผ ๊ฐ์ด ์ผ์ต๋๋ค.
๋น์ ๋ง์ด ๋ง์์ @SimenB https://github.com/simenb , ๋ด๊ฐ ๋ญ๊ฐ๋ฅผ ๋ฐ๊ฟจ์ต๋๋ค
๋ด ํ ์คํธ์์ Sinon์ผ๋ก ์ ํํ๋ ์ฌ์ด์ ํต๊ณผํ ๊ฒ์ฒ๋ผ ๋ณด์ ๋๋ค.
๋๋๋ ธ์ ๋ ์ฌ์ ํ ์๋ํ์ง ์์ต๋๋ค. ๋ฌธ์ ๊ฐ ์๋๊ฑฐ ๊ฐ์๋ฐ
ํด๊ฒฐ๋์์ต๋๋ค.โ
๋น์ ์ด ์ธ๊ธ๋์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ฐ๋ ๊ฒ์ ๋๋ค.
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ณ GitHub์์ ํ์ธ
https://github.com/facebook/jest/issues/936#issuecomment-352488400 ๋๋ ์์๊ฑฐ
์ค๋ ๋
https://github.com/notifications/unsubscribe-auth/AQRY9a5-s2_bjCWKNw5WiAJW-JeBf8W3ks5tBpoygaJpZM4IPGAH
.
--
๋๋ฐ ํฌ๋ ์ค์ฐ
๊ณ์ฝ ๊ฐ๋ฐ์ | ๊ฐ๋ฐ์ ์ ํ
์ด๋ฉ์ผ: [email protected]
ํธ๋ํฐ:
์น์ฌ์ดํธ: http://www.develer.co.uk
์ด ์ ์ ๋ฉ์ผ์ ์ธ์ํ๊ธฐ ์ ์ ํ๊ฒฝ์ ์๊ฐํ์ญ์์ค
๊ฒฝ๊ณ : ์ปดํจํฐ ๋ฐ์ด๋ฌ์ค๋ ์ด๋ฉ์ผ์ ํตํด ์ ์ก๋ ์ ์์ต๋๋ค. ๋ฐ๋ ์ฌ๋
์ด ์ด๋ฉ์ผ๊ณผ ๋ชจ๋ ์ฒจ๋ถ ํ์ผ์ ๋ฐ์ด๋ฌ์ค๊ฐ ์๋์ง ํ์ธํด์ผ ํฉ๋๋ค.
Developer Limited๋ ๋ฐ์ด๋ฌ์ค๋ก ์ธํ ํผํด์ ๋ํด ์ฑ
์์ ์ง์ง ์์ต๋๋ค.
์ด ์ด๋ฉ์ผ๋ก ์ ์ก๋ฉ๋๋ค. ์ด๋ฉ์ผ ์ ์ก์ ๋ณด์ฅํ ์ ์์
์ ๋ณด๊ฐ ๊ฐ๋ก์ฑ๊ฑฐ๋, ์์๋๊ฑฐ๋, ์์ค๋ ์ ์์ผ๋ฏ๋ก ์์ ํ๊ฑฐ๋ ์ค๋ฅ๊ฐ ์์ต๋๋ค.
ํ๊ดด๋๊ฑฐ๋ ๋ฆ๊ฒ ๋์ฐฉํ๊ฑฐ๋ ๋ถ์์ ํ๊ฒ ๋์ฐฉํ๊ฑฐ๋ ๋ฐ์ด๋ฌ์ค๋ฅผ ํฌํจํฉ๋๋ค. ๋ฐ์ ์ธ
๋ฐ๋ผ์ ๋ณธ ๋ฌธ์์ ์ค๋ฅ๋ ๋๋ฝ์ ๋ํด ์ฑ
์์ ์ง์ง ์์ต๋๋ค.
์ด๋ฉ์ผ ์ ์ก์ ๊ฒฐ๊ณผ๋ก ๋ฐ์ํ๋ ์ด ๋ฉ์์ง์ ๋ด์ฉ.
๊ฒฝ๊ณ : Develer Limited๊ฐ ํฉ๋นํ ์๋ฐฉ ์กฐ์น๋ฅผ ์ทจํ์ง๋ง
์ด ์ด๋ฉ์ผ์ ๋ฐ์ด๋ฌ์ค๊ฐ ์๋์ง ํ์ธํ์ญ์์ค. ํ์ฌ๋ ์๋ฝํ ์ ์์ต๋๋ค.
์ด ์ด๋ฉ์ผ ๋๋
์ฒจ๋ถ ํ์ผ.
Developer Limited๋ ์๊ธ๋๋์ ์จ์ผ์ฆ์ ๋ฑ๋ก๋ ์ ํ ํ์ฌ์
๋๋ค. |
์ฌ์
์๋ฑ๋ก๋ฒํธ 09817616 | ๋ฑ๋ก ์ฌ๋ฌด์ค: SUITE 1 SECOND
FLOOR EVERDENE HOUSE, DEANSLEIGH ROAD, ๋ณธ๋จธ์ค, ์๊ตญ, BH7 7DU
ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๋ํด @ainesophaur ์๊ฒ ๊ฐ์ฌ๋๋ฆฝ๋๋ค.
๋๊ตฌ๋ ์ง ๋น๋๊ธฐ์ ์์ ์ด ์๋ ์์ ๊ฐ ์ ์ฉํ๋ค๊ณ ์๊ฐํ๋ ๊ฒฝ์ฐ ์ฌ๊ธฐ์ ๋ด ๊ฒ์ด ์์ต๋๋ค.
//reportError.js
const functions = {};
functions._reportToServer = (error, auxData) => {
// do some stuff
};
functions.report = (error, auxData = {}) => {
if (shouldReportToServer()) {
functions._reportToServer(error, auxData);
}
};
export default functions;
// reportError.jest.js
import reportError from 'app/common/redux/lib/reportError';
reportError._reportToServer = jest.fn();
describe('test reportError', () => {
it('reports ERROR to server as as error', () => {
reportError.report(new Error('fml'), {});
expect(reportError._reportToServer).toHaveBeenCalledTimes(1);
});
});
@jim-moody ๋ฌธ์ ๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ดํดํ๋ค๋ฉด ๊ทํ์ ์์์ ๋ค์๊ณผ ๊ฐ์ด ์๋ํฉ๋๋ค.
const spyOnExampleFunc2 = jest.spyOn(example, 'func2');
example.func1();
expect(spyOnExampleFunc2).toBeCalled();
(์ด _only_๋ ์์ ์ ๊ฐ์ด ํจ์๋ฅผ const๋ก ๋ด๋ณด๋ด๋ ๊ฒฝ์ฐ์๋ง ์๋ํฉ๋๋ค.)
@dinvlad ๋์ ์์ !
@dinvlad ์ ๋ต๋ณ์ ๊ฐ์ฒด ํ์ด์ง ์ ๋ค์ ๋ชจ์ ๊ด๋ จ ๋ฌธ์๋ฅผ ์ถ๊ฐํ๊ฑฐ๋, ์์๋ก ๋ณด์ฌ์ฃผ๊ฑฐ๋, ๋ชจ์ ๊ธฐ๋ฅ ํ์ด์ง ์ ์ฐ๊ฒฐํ๋ฉด
๋ด ์ฌ์ฉ ์ฌ๋ก๋ jest์ ์๋ก์ด ์ฌ์ฉ์๋ก์ ์ผ๋ถ mocha + sinon.js ์ฝ๋๋ฅผ jest๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ๋ ๊ฒ์ ๋๋ค. ์ด๋ฏธ ๊ฐ์ฒฉ๊ณผ ๊ธฐ๋๊ฐ ์์๊ธฐ์ ์ฌ์ธ ์ค ์์๋ค. ๊ทธ๋ฌ๋ ์ด ์ค๋ ๋๋ฅผ ์ฝ๊ณ Mock ํจ์์ ๋ํ jest ๋ฌธ์๋ฅผ ์ฝ์ ํ ์ ๋ jest๋ฅผ ์ด๋ฐ ์์ผ๋ก ์ฌ์ฉํ๋ ค๋ฉด ํ ์คํธ๋ฅผ ๋ค์ ์์ฑํ๊ฑฐ๋ ESM ๋๋ Babel์ ๋ํ ์์ธํ ์ดํด๊ฐ ํ์ํ๊ฑฐ๋ ๊ธฐํ ํผ๋์ด ํ์ํ ์ ์๋ค๋ ์ธ์์ ๋ฐ์์ต๋๋ค.
Jest ๋๋ถ์ ํ ์คํธ๋ฅผ ๋ ์ฝ๊ฒ ์์ฑ/์ดํดํ๊ณ ๋ ๋น ๋ฅด๊ฒ ์คํํ ์ ์์ต๋๋ค. :)
๋ฌธ์๋ฅผ ๋ช ํํ ํ๋ PR์ ๋งค์ฐ ํ์ํฉ๋๋ค! ๐
ES ๋ชจ๋ ๊ตฌ๋ฌธ์ผ๋ก ํน์ ๋ชจ๋๋ง ๋ชจ์ํ๋ ค๋ฉด require.requireActual ์ ์ฌ์ฉํ์ฌ ์๋ ๋ชจ๋์ ๋ณต์ํ ๋ค์ ๋ชจ์ํ๋ ค๋ ๋ชจ๋์ ๋ฎ์ด์ธ ์ ์์ต๋๋ค.
import { foo } from './example';
jest.mock('./example', () => (
...require.requireActual('./example'),
foo: jest.fn()
));
test('foo should be a mock function', () => {
expect(foo('mocked!')).toHaveBeenCalledWith('mocked!');
});
๊ฑฐ๊พธ๋ก ๋ ๋๋์ด ๋ค์ง๋ง ๋ด๊ฐ ๋ง๋ ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ๋๋ค. @joshjg์ ๋ํ ๋ชจ์ ํ.
๊ธด ํ ๋ก ์์ ๊ธธ์ ์์์ต๋๋ค. ๋ฐฉ๊ธ ์ง๋ฌธ์ด ์์ต๋๋ค. ์ด์จ๋ ํจ์์ ์ค์ ๊ตฌํ์ด ํธ์ถ๋๋์ง ํ ์คํธํ ์ ์์ต๋๊น?
๋ด๊ฐ ์ดํดํ๋ ๋ฐ์ ๋ฐ๋ฅด๋ฉด jest.fn()
์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ ์๋ ๊ธฐ๋ฅ์ ์ฌ์ ์ํ์ง๋ง ์ฌ์ฉํ์ง ์์ผ๋ฉด ์ฝ์์์ jest.fn() function or a spy
์ฌ์ผ ํ๋ค๋ ์ค๋ฅ๊ฐ ํ์๋ฉ๋๋ค.
์์ฒญ์ด ์ ๋ฌ๋ ๋ฏธ๋ค์จ์ด๋ฅผ ํ ์คํธํ๋ ค๊ณ ํ๋ฏ๋ก ์กฐ๋กฑํ๋ฉด ๋ชจ๋ ๋ ผ๋ฆฌ๊ฐ ์์ค๋๊ณ ๋ฐ์ดํฐ๊ฐ ๋ค์ ๋ฏธ๋ค์จ์ด๋ก ์ ๋ฌ๋์ง ์์ต๋๋ค. ์กฐ๋กฑํ์ง ์๊ณ ๊ฐ์ ธ์ค๋ฉด ์ด ํจ์๊ฐ ํธ์ถ๋์๋์ง ํ ์คํธํ ์ ์์ต๋๊น?
jest.spyOn
๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๊น? ๊ธฐ๋ณธ์ ์ผ๋ก ๊ธฐ๋ณธ ํจ์๋ฅผ ํธ์ถํฉ๋๋ค.
๋์์ ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. ์๋ํ์ง๋ง ํ ์คํธ ๊ฒฐ๊ณผ console.log๋ฅผ ๋ฃ๊ณ ์ธ์ํ๊ธฐ ๋๋ฌธ์ ํธ์ถ๋์์ง๋ง ํ ์คํธ์ ๋ฐ๋ฅด๋ฉด ํธ์ถ๋์ง ์์์ต๋๋ค.
ํ ์คํธ ํ์ผ
import errorHandler from '../../controller/errorHandler'
describe('auth test', () => {
describe('test error: ', () => {
const test1 = jest.spyOn(errorHandler, 'handleClientError')
test('should return 400', (done) => {
request(app)
.post('/auth/error')
.then((res) => {
expect(res.statusCode).toBe(400)
expect(test1).toBeCalled()
done()
})
})
์ค๋ฅ ํธ๋ค๋ฌ
module.exports = {
handleClientError () {
console.log('err')
}
}
์ฝ์
console.log src/controller/errorHandler.js:10
err
โ auth test โบ test error: โบ should return 400
expect(jest.fn()).toBeCalled()
Expected mock function to have been called.
18 | expect(res.statusCode).toBe(400)
> 19 | expect(test1).toBeCalled()
20 | done()
21 | })
22 | })
ํจ์๊ฐ handleClientError
๋๋ logError
์
๋๊น?
@WangHansen ๊ทํ์ ์์ ์์ ๊ทํ์ ์ฝ๋๋ expect(errorHandler.handleClientError).toBeCalled() // > true
์ด์ด์ผ ํฉ๋๋ค.
@WangHansen .mockImplementation()
์ jest.spyOn()
์์ต๋๊น? Jasmine์์ ์จ ์ฌ๋์ผ๋ก์ ์ ๋ ์ด ํ์ด Jasmine์ ์คํ์ด์ ๋์ผํ ๊ธฐ๋ฅ์ ๋ฌ์ฑํ๋ ๋ฐ ์ค์ํ๋ค๋ ๊ฒ์ ์์์ต๋๋ค. ์
const mockModuleFunction = jest
.spyOn(module, 'function')
.mockImplementation(() => 'hello');
...
expect(mockModuleFunction.mock).toBeCalled();
mockImplementation()
์ฌ์ฉํ์ง _์์ผ๋ฉด_ jest.spyOn()
๋ ๋ชจ์(afaiu)๊ฐ _์๋_ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ์ค์ ๋ก ๊ธฐ๋ณธ ๊ตฌํ์ ๋ฐ๋ฆ
๋๋ค. ๊ธฐ๋ณธ ๊ตฌํ์ ์ ์งํด์ผ ํ๋ ๊ฒฝ์ฐ ์ฌ์ฉํ ๊ฐ์น๊ฐ ์์ ์ ์์ต๋๋ค.
const moduleFunction = module.function;
jest.spyOn(module, 'function').mockImplementation(moduleFunction);
...
์ด๊ฒ์ด ๊ผญ ํ์ํ์ง๋ ํ์คํ์ง ์์ง๋ง _๋ฐ๋์_ ์๋ํด์ผ ํฉ๋๋ค.
์๋ ์์ฒญ์ผ๋ก ๋์๊ฐ๊ธฐ...
import * ์ฃผ์์ ํ๋ก์๋ฅผ ๋ํํ ์ ์์ต๋๊น? ์
'./myfile.js'์์ ํ ์คํธ๋ก * ๊ฐ์ ธ์ค๊ธฐ;
const ํธ๋ค๋ฌ = {
/** ๊ฐ๋ก์ฑ๊ธฐ: ์์ฑ ๊ฐ์ ธ์ค๊ธฐ */
get(๋์, propKey, ์์ ๊ธฐ) {
console.log( GET ${propKey}
);
๋ฐํ 123;
},
/** Intercepts: checking whether properties exist */
has(target, propKey) {
console.log(`HAS ${propKey}`);
return true;
}};
const p = ์๋ก์ด ํ๋ก์(ํ ์คํธ);
2018๋
1์ 30์ผ ํ์์ผ ์คํ 4:24 Denis Loginov [email protected]
์ผ๋ค:
@WangHansen https://github.com/wanghansen ์ถ๊ฐํ ์ ์๋์?
.mockImplementation() to your jest.spyOn()? ์์ ์ค๋ ์ฌ๋์ผ๋ก์
Jasmine, ๋๋ ์ด ํ์ด
์ฌ์ค๋ฏผ์ ์คํ์ด. ์const mockModuleFunction = ๋๋ด
.spyOn(๋ชจ๋, 'ํจ์')
.mockImplementation(() => '์๋ ํ์ธ์');...expect(mockModuleFunction.mock).toBeCalled();mockImplementation()์ ์ฌ์ฉ ํ์ง ์์ผ๋ฉด jest.spyOn()์ด ์์ฑํฉ๋๋ค.
๋ชจ์(afaiu)๊ฐ ์๋ ๊ฐ์ฒด์ด๋ฉฐ ์ค์ ๋ก ๋ค์ดํฐ๋ธ
๊ตฌํ. ๋ค์ดํฐ๋ธ ๊ตฌํ์ ์ ์งํด์ผ ํ๋ค๋ฉด ์๋ง๋
์ฌ์ฉํ ๊ฐ์นconst moduleFunction = module.function;jest.spyOn(๋ชจ๋, 'ํจ์').mockImplementation(moduleFunction);...
์ด๊ฒ์ด ํ์ํ์ง๋ ํ์คํ์ง ์์ง๋ง ์๋ ํด์ผ ํ๋ค๋ ๊ฒ์ ํ์คํฉ๋๋ค.
โ
๋น์ ์ด ์ธ๊ธ๋์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ฐ๋ ๊ฒ์ ๋๋ค.
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ณ GitHub์์ ํ์ธ
https://github.com/facebook/jest/issues/936#issuecomment-361648414 ๋๋ ์์๊ฑฐ
์ค๋ ๋
https://github.com/notifications/unsubscribe-auth/AQRY9VXyHNYatwOOY6EV637WGQH9k5Plks5tP0I9gaJpZM4IPGAH
.
--
๋๋ฐ ํฌ๋ ์ค์ฐ
๊ณ์ฝ ๊ฐ๋ฐ์ | ๊ฐ๋ฐ์ ์ ํ
์ด๋ฉ์ผ: [email protected]
ํธ๋ํฐ:
์น์ฌ์ดํธ: http://www.develer.co.uk
์ด ์ ์ ๋ฉ์ผ์ ์ธ์ํ๊ธฐ ์ ์ ํ๊ฒฝ์ ์๊ฐํ์ญ์์ค
๊ฒฝ๊ณ : ์ปดํจํฐ ๋ฐ์ด๋ฌ์ค๋ ์ด๋ฉ์ผ์ ํตํด ์ ์ก๋ ์ ์์ต๋๋ค. ๋ฐ๋ ์ฌ๋
์ด ์ด๋ฉ์ผ๊ณผ ๋ชจ๋ ์ฒจ๋ถ ํ์ผ์ ๋ฐ์ด๋ฌ์ค๊ฐ ์๋์ง ํ์ธํด์ผ ํฉ๋๋ค.
Developer Limited๋ ๋ฐ์ด๋ฌ์ค๋ก ์ธํ ํผํด์ ๋ํด ์ฑ
์์ ์ง์ง ์์ต๋๋ค.
์ด ์ด๋ฉ์ผ๋ก ์ ์ก๋ฉ๋๋ค. ์ด๋ฉ์ผ ์ ์ก์ ๋ณด์ฅํ ์ ์์
์ ๋ณด๊ฐ ๊ฐ๋ก์ฑ๊ฑฐ๋, ์์๋๊ฑฐ๋, ์์ค๋ ์ ์์ผ๋ฏ๋ก ์์ ํ๊ฑฐ๋ ์ค๋ฅ๊ฐ ์์ต๋๋ค.
ํ๊ดด๋๊ฑฐ๋ ๋ฆ๊ฒ ๋์ฐฉํ๊ฑฐ๋ ๋ถ์์ ํ๊ฒ ๋์ฐฉํ๊ฑฐ๋ ๋ฐ์ด๋ฌ์ค๋ฅผ ํฌํจํฉ๋๋ค. ๋ฐ์ ์ธ
๋ฐ๋ผ์ ๋ณธ ๋ฌธ์์ ์ค๋ฅ๋ ๋๋ฝ์ ๋ํด ์ฑ
์์ ์ง์ง ์์ต๋๋ค.
์ด๋ฉ์ผ ์ ์ก์ ๊ฒฐ๊ณผ๋ก ๋ฐ์ํ๋ ์ด ๋ฉ์์ง์ ๋ด์ฉ.
๊ฒฝ๊ณ : Develer Limited๊ฐ ํฉ๋นํ ์๋ฐฉ ์กฐ์น๋ฅผ ์ทจํ์ง๋ง
์ด ์ด๋ฉ์ผ์ ๋ฐ์ด๋ฌ์ค๊ฐ ์๋์ง ํ์ธํ์ญ์์ค. ํ์ฌ๋ ์๋ฝํ ์ ์์ต๋๋ค.
์ด ์ด๋ฉ์ผ ๋๋
์ฒจ๋ถ ํ์ผ.
Developer Limited๋ ์๊ธ๋๋์ ์จ์ผ์ฆ์ ๋ฑ๋ก๋ ์ ํ ํ์ฌ์
๋๋ค. |
์ฌ์
์๋ฑ๋ก๋ฒํธ 09817616 | ๋ฑ๋ก ์ฌ๋ฌด์ค: SUITE 1 SECOND
FLOOR EVERDENE HOUSE, DEANSLEIGH ROAD, ๋ณธ๋จธ์ค, ์๊ตญ, BH7 7DU
@dinvlad @iampeterbanjo @SimenB ๋ชจ๋ ๋์์ ๋ค์ ํ ๋ฒ ๊ฐ์ฌ๋๋ฆฝ๋๋ค. ํ์ง๋ง ์ํ๊น๊ฒ๋ ์ ์ํ์ ๋ฐฉ๋ฒ ์ค ์ด๋ ๊ฒ๋ ํจ๊ณผ๊ฐ ์์์ต๋๋ค. next(err)
ํ์์ผ๋ก ํจ์๊ฐ ํธ์ถ๋๊ธฐ ๋๋ฌธ์ธ์ง ๊ถ๊ธํฉ๋๋ค. ์์ฒญ์ด ์คํจํ์ ๋ ๋
ผ๋ฆฌ๋, ๊ทธ๊ฒ์์ ์ ๋ฌ ๋ ๊ฒ์
๋๋ค errorHandler
ํธ์ถํ์ฌ return next(err)
. console.log
์ถ๊ฐํ์ ๋ ์ธ์ ์ค์ด๊ธฐ ๋๋ฌธ์ ํจ์๊ฐ ํธ์ถ๋๊ณ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ํ
์คํธ์ ๋ฐ๋ฅด๋ฉด ๊ฒฐ์ฝ ํธ์ถ๋์ง ์์ต๋๋ค.
@dinvlad ๋๋ ๋น์ ์ ๋ฐฉ๋ฒ์ ์๋ํ์ง๋ง ์๋ํ์ง ์์์ง๋ง ์ด์จ๋ ๋์์ ์ฃผ์
์ ๊ฐ์ฌํฉ๋๋ค. jest.spyOn ์ ๊ณต์ ๋ฌธ์์ ๋ฐ๋ฅด๋ฉด ์ mockImplementation
๋ฅผ ํธ์ถํด์ผ ํ๋์ง ๊ถ๊ธํฉ๋๋ค. ์๋ ํจ์๋ฅผ ์ฌ์ ์ํ๋ ค๋ ๊ฒฝ์ฐ์๋ง ํธ์ถํฉ๋๋ค.
@WangHansen ์, ์๋ ๋ฐฉ๋ฒ์ ์ฌ์ ์ํ๋ ค๋ ๊ฒฝ์ฐ์๋ง ํ์ํ๋ค๋ ๊ฒ์ด ๋ง์ต๋๋ค. ๋๋ ๋จ์ง ๊ทธ ์ํฉ์ ๋ํ ์์ด๋์ด๋ฅผ ๋์ง๊ณ ์์๋ค.
์คํจํ์ ์ ์๋ ํ ๊ฐ์ง ์ด์ ๋ ๋น๋๊ธฐ์ฑ์
๋๋ค. ๋ฉ์๋๊ฐ ์ฝ๋ฐฑ ๋ฐ/๋๋ ์ฝ์(๋๋ async/await)์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ํ
์คํธ ๋ฉ์๋๊ฐ ์ข
๋ฃ๋๊ธฐ ์ ์ ์์์ด ์ค์ ๋ก ์คํ๋๋์ง ํ์ธํด์ผ ํฉ๋๋ค. ๊ทธ๊ฒ์ ์ฃผ์ฅํ๋ ํน๋ณํ ๋ฐฉ๋ฒ expect.assertions(N)
์ด ์์ต๋๋ค. ๋ํ ์ฝ๋ฐฑ/์ฝ์ ๋ด๋ถ์ ์ฝ๋๊ฐ ํธ์ถ๋ ํ์๋ง ์์์ด ์คํ๋๋์ง ํ์ธํ์ญ์์ค. ๋๋ ๋น์ ์ด ๊ทธ๊ฒ์ ๋ณด์์ ๊ฒ์ด๋ผ๊ณ ํ์ ํ์ง๋ง ์ฐธ์กฐ ์ฉ์ผ๋ก https://facebook.github.io/jest/docs/en/asynchronous.html
@seibelj๊ฐ ์ค๋ช ํ๋ ๊ฒ์ฒ๋ผ ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉ๋๋ fns๋ฅผ ์กฐ๋กฑํ๋ ๊ฒ์ ๋ชจ๋ impl์ ๋ณ๊ฒฝํ์ง ์๊ณ ๋ ๋ถ๊ฐ๋ฅํฉ๋๋ค.
๋ด ์๊ฐ์ ํ ์คํธ๋ ๋น์ฆ๋์ค ๋ก์ง์ด ๊ตฌํ๋๋ ๋ฐฉ์์ ์ฃผ๋ํด์๋ ์ ๋ฉ๋๋ค ๐ (์ ์ด๋ ์ด ์ ๋๊น์ง๋ ์๋)
๋๋ด์ผ๋ก ๊ทธ๋ฌํ ํ๋์ ๊ตฌํํ ๊ณํ์ด ์์ต๋๊น?
์๋
ํ์ธ์,
์ ์ฒด ํ ๋ก ์ ๋ค์ ๋ฆ์์ง๋ง ์ ์ฒด ํ ๋ก ์ ํตํด ์ฝ์์ต๋๋ค. ์์ง๊น์ง๋ ๊ทธ๋ ๊ฒ ํ์ง ๋ชปํ์ต๋๋ค.
@greypants ์ ์ ๋งํ ์๋ฃจ์
์ ์ฌ์ ํ โโ์๋ ๊ธฐ๋ฅ์ ํธ์ถํ๊ธฐ ๋๋ฌธ์ ์ค์ ๋ก ์๋ํ์ง ์์์ต๋๋ค.
์ด ํ ๋ก ์ ์์ํ ์ดํ๋ก ๋ณ๊ฒฝ๋ ์ฌํญ์ด ์์ต๋๊น? ๋ด๊ฐ ๋ญ๊ฐ๋ฅผ ๋์น๊ณ ์์ต๋๊น?
๋๋ @greypants ์ ์๋ฃจ์ ์ ์ฝ๊ฐ ์์ ํ๊ณ ๊ทธ๊ฒ์ ๋๋ฅผ ์ํด ์ผํ์ต๋๋ค. ๋๊ตฐ๊ฐ๋ฅผ ๋์ธ ์ ์๋ค๋ฉด ๋ด ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
import Module from './module'
import { getJSON } from './helpers'
jest.mock('./helpers', () =>
Object.assign(require.requireActual('./helpers'), {
getJSON: jest.fn()
})
)
test('should use the mock', async () => {
getJSON.mockResolvedValue('foo')
await expect(module().someAsyncThingThatUsesGetJSON()).resolves.toEqual('foo')
})
test('should use the actual module', () => {
expect(module().someFunctionThatUsesAHelper()).toBe(true)
})
์ด๊ฒ์ ์ฌ์ ํ โโ์ข ํดํคํ๊ณ ๋ฌธ์๊ฐ ๋์์ด๋์ง ์์์ต๋๋ค. Jest์ ๊ทธ ๋ค์ ์๋ ํ์ ๊ฐ์ฌํ์ง๋ง ์ต์ํ "๊ณต์์ ์ธ" ์๋ฃจ์ ์ด ์์ด์ผ ํ๋ ์ผ๋ฐ์ ์ธ ์ฌ์ฉ ์ฌ๋ก์ฒ๋ผ ๋ณด์ ๋๋ค.
@sarahdayan , ๊ทธ๋ฆฌ๊ณ ๊ด์ฌ ์๋ ์ฌ๋ -
๋๋ babel-plugin-rewire๋ฅผ ์ฌ์ฉํ๊ฒ ๋์์ต๋๋ค.
๊ทธ ํ๋ฌ๊ทธ์ธ์ ์ฐพ๋ ๋ฐ ์๊ฐ์ด ๊ฑธ๋ ธ์ง๋ง 'ํต์ฌ์ด๋ผ๊ณ ๋๋ผ์ง ์์ ๋งํผ ์ผ๊ด๋ ์๋ฃจ์
์ด์์ต๋๋ค.
๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์ฐ๋ฆฌ๋ ๋ชจ๋์์ ํ๋ ์ด์์ ๊ธฐ๋ฅ์ ๋ชจ์ํ๊ณ ์ถ์ง ์์ต๋๋ค . global jest์ ์กฐ๋กฑ ์์คํ
์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ genMockFromModule
๋ฐ requireActual
์ฌ์ฉํ์ฌ ์ด๋ฅผ ๋ฌ์ฑํ ์ ์์ต๋๋ค. ์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
// __mocks__/myModule.js
const moduleMock = jest.genMockFromModule('./../myModule');
// in most cases we don't want to mock `someFunction`
moduleMock.someFunction = jest.fn(
(...args) => require.requireActual('./../myModule').someFunction(...args)
)
module.exports = moduleMock;
์ด ์๋ฃจ์
์ ์ ์ฒด ๋ชจ๋์ด ๋ชจ์๋ ๋ someFunction
์๋ ๊ตฌํ์ ์ฌ์ฉํ์ฌ ๋ชจ๋์ ๋ค๋ฅธ ๊ธฐ๋ฅ์ ๋ํ ๋ชจ์ ์ฌ์ฉ์ ํ์ฉํ๊ณ mockImplementationOnce
๋๋ mockImplementation
ํ์ฌ someFunction
ํจ์๋ฅผ ๋ชจ์ํ ์๋ ์์ต๋๋ค mockImplementation
API.
์์ ๋ชจ๋ ๋ํ๋ฅผ ์ฝ์์ง๋ง ๋์๊ฒ ๋ง๋ ์๋ฃจ์
์ ์์ต๋๋ค.
์ด ํ
์คํธ ์ผ์ด์ค์ ๋ํ ์๋ฃจ์
์ ์ฌ์ ํ ์ฐพ๊ณ ์๋ค๋ฉด, ๋๋ต์ babel-plugin-rewire ์
๋๋ค . ์ด ํ๋ฌ๊ทธ์ธ์ ์ฐ๋ฆฌ๊ฐ ๋
ผ์ํ ์๋๋ฆฌ์ค ์ผ์ด์ค๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๊ฒ์
๋๋ค.
๊ทธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ดํด๋ณด์ญ์์ค. ๋์ค์ ๋ค์ ๊ฐ์ฌํ ๊ฒ์
๋๋ค.
์์ ์ ์ฒด ์ค๋ ๋๋ฅผ ์์ฝํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
f
, g
๋ฐ h
ํจ์๊ฐ ์๋ m
๋ชจ๋์ด ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค. ์ฌ๊ธฐ์ g
๋ฐ h
ํธ์ถ f
. ์ฐ๋ฆฌ๋ g
์ h
๊ฐ ์ค์ f
๋์ ์ mock์ ํธ์ถํ๋๋ก f
๋ฅผ ๋ชจ์ํ๊ณ ์ถ์ต๋๋ค. ๋ถํํ๊ฒ๋์ด ์ง์ ํ์ง ์๋ ํ ํ ์ ์์ต๋๋ค f
ํญ์ ํตํด์ด๋ผ๊ณ exports
๋ก cpojer ์ค๋ช
. ์ด๊ฒ์ ๋ชจ๋์ด TypeScript์์ ES6 ๊ฐ์ ธ์ค๊ธฐ/๋ด๋ณด๋ด๊ธฐ ๊ตฌ๋ฌธ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ๋ถ๊ฐ๋ฅํฉ๋๋ค(๋ฐ๋ฒจ์์๋ ๋ง์ฐฌ๊ฐ์ง์
๋๋ค).f
๋ฅผ ๋ค๋ฅธ ๋ชจ๋ m2
๋ก ์ด๋ํ๋ค๊ณ ๊ฐ์ ํฉ๋๋ค. ๊ทธ๋ฌ๋ฉด m
๋ import {f} from 'm2'
์ ๊ฐ์ ๋ช
๋ น๋ฌธ์ ๊ฐ๊ฒ ๋๋ฉฐ g
๋ฐ h
๊ฐ f
๋ฅผ ํธ์ถํ ๋ ์ค์ ๋ก๋ m2.f
ํธ์ถํฉ๋๋ค m2 = require('./m2')
(๋ฐ๋ฒจ/TypeScript ๋ฒ์ญ์ด ํ์๋๋ ๋ฐฉ์์
๋๋ค). ์ด๋ ๊ฒ ํ๋ฉด greypants ์์ ์ค๋ช
ํ ๋๋ก f
์์ ์ ์ผ๋ก ์กฐ๋กฑํ ์ ์์ต๋๋ค. ์ฆ, ๋ชจ๋ ๊ฒฝ๊ณ๋ฅผ ๋๋ ๊ฒฝ์ฐ์๋ง ํธ์ถ์ ์์ ์ ์ผ๋ก ๋ชจ์ํ ์ ์์ต๋๋ค . ์ฐธ๊ณ : greypants์ ์๋ฃจ์
์ ์ด์ " jest.mock()
์ ๋ชจ๋ ํฉํ ๋ฆฌ๋ ๋ฒ์๋ฅผ ๋ฒ์ด๋ ๋ณ์๋ฅผ ์ฐธ์กฐํ ์ ์์ต๋๋ค. - ์๋ชป๋ ๋ณ์ ์ก์ธ์ค: __assign"์ด๋ผ๋ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ์์ฑํฉ๋๋ค. ๋๋ ์ด๊ฒ์ด Jest์ ๋ฒ๊ทธ๋ผ๊ณ ์๊ฐํฉ๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด ์๋์ ๊ฐ์ด Object.assign
๋ฅผ ์ฌ์ฉํ์ญ์์ค.(2)์ ์:
~~~js
// ๋ชจ๋ m.js
'./m2'์์ {f} ๊ฐ์ ธ์ค๊ธฐ
๋ด๋ณด๋ด๊ธฐ ํจ์ g() { ๋ฐํ 'f ๋ฐํ ' + f(); };
// ๋ชจ๋ m2.js
export function f() { return '์ค์ f'; }
// ํ
์คํธ.js
*๋ฅผ './m'์์ m์ผ๋ก ๊ฐ์ ธ์ค๊ธฐ
jest.mock('./m2', () => Object.assign(
require.requireActual('./m2'), {
f: jest.fn().mockReturnValue('๋ชจ์')
}));
test('์กฐ๋กฑ', () => {
๊ธฐ๋(mg()).toEqual('๋ชจ์ ๋ฐํ');
});
~~~
์ด๊ฒ์ ํ
์คํธํ๋ ๋์ #2649: ํ
์คํธ ๋ด์์ jest.mock
๋ฅผ ํธ์ถํด๋ ์๋ฌด๋ฐ ํจ๊ณผ๊ฐ ์์ผ๋ฉฐ ์ ์ญ ๋ฒ์์์ ํธ์ถํ๋ฉด ๋ค๋ฅธ ํ
์คํธ ์ ์ unmock
ํ ์ ์์ต๋๋ค. ๋งค์ฐ ์ฑ๊ฐ์๋ค.
๊ฐ์ฌ ํด์!! @sarahdayan
์ด๊ฒ์ ์ ์ ๋์ ์ฐพ๊ณ ์์๋ค
๋ฌธ์๊ฐ ๋ถ์กฑํ ๊ฒฝ์ฐ PR์ ํญ์ ๋ฌธ์๋ฅผ ๋ช ํํ ํ๋ ๊ฒ์ ํ์ํฉ๋๋ค ๐
์๋ ํ์ธ์ ์ฌ๋ฌ๋ถ!
๋๋ ์กฐ๊ธ ๋์๊ณ ๊ทธ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ ์์ด๋์ด๋ฅผ ๋์ต๋๋ค.
// m1.js
export const f = () => "original f"
// __mocks__/m1.js
const originalM1 = require.requireActual("../m1");
// set the original as a prototype
let mockModule: any = Object.create(originalM1);
const __setMock = (name, value) => {
mockModule[name] = value;
};
const __removeMock = (name) => {
Reflect.deleteProperty(mockModule, name);
};
// enhance the mocked module to allow overriding original exports
module.exports = Object.assign(mockModule, { __setMock, __removeMock });
// m1.test.js
import { f } from "./m1";
jest.mock("./m1");
test("mocking stuff", () => {
// here nothing is mocked - the original module is used
expect(f()).toBe("original f");
// override the export f of the module
require("./m1").__setMock("f", () => "mocked f");
expect(f()).toBe("mocked f");
// set it back to the original
require("./m1").__removeMock("f");
expect(f()).toBe("original f");
//override with another value
require("./m1").__setMock("f", () => "another mocked f");
expect(f()).toBe("another mocked f");
});
๋ด 2์ผํธ:
๋๋ ๋ง์ ์๋ฃจ์ ์ ํ ์คํธํ๊ณ (๋ชจ๋๋ ์๋์ง๋ผ๋) ๋๋ฅผ ์ํด ์ผํ ์ ์ผํ ์๋ฃจ์ ์ ์ด๊ฒ์ ๋๋ค(jest 23).
// importedModule.js
export const funcB = () => {
return "not mocked";
};
export const funcA = () => {
return mockProxy.funcB(); // this works but it's soooo hacky
};
export const mockProxy = {
funcB
};
// moduleToTest.js
import { funcA } from "./moduleImported.js";
export default function() {
return funcA();
}
// test
let moduleImported = require("./moduleImported.js");
moduleImported.mockProxy.funcB = jest.fn(() => "mocked");
const funcToTest = require("./moduleToTest.js").default; // or import
it("should return mocked", function() {
expect(funcToTest()).toBe("mocked");
});
๋๋ ๋ค์๊ณผ ๊ฐ์ ์ด์ ๋ก ์ด๊ฒ์ ์๋ํ๋ ๊ฒ์ ์ข์ง ์๋ค๋ ๊ฒฐ๋ก ์ ๋๋ฌํ์ต๋๋ค.
test.js
๋ importedModule.js
์ ๊ตฌํ ์ธ๋ถ์ ๋ณด์ ๋ํด ๋๋ฌด ๋ง์ด ์๊ณ ์์ต๋๋ค.mockProxy
๋ณด๋ฉด importedModule.js
๋๊ตฌ๋ ์ง ์๋ํ๋ ์๋ฃจ์ ์ ์ฐพ์์ต๋๊น?
๋๋ ์ฌ์ฉํ๊ณ ์๋ค :
"jest": "^21.2.1",
"jest-cli": "^21.2.1",
@jamesone https://github.com/facebook/jest/issues/936#issuecomment -410080252 ์ฝ์ด ๋ณด์ จ๋์?
์ด๊ฒ์ @thomaskempel ์ ๋ต๋ณ์ ๊ธฐ๋ฐ์ผ๋ก ์ ์๊ฒ
์ ๊ฒฝ์ฐ์๋ node_modules์์ ์ข ์์ฑ์ ์กฐ๋กฑํ๊ณ ์ถ์์ต๋๋ค. ์ด๋ฅผ '๊ณต์ ๊ตฌ์ฑ ์์'๋ผ๊ณ ๋ถ๋ฅผ ์ ์์ต๋๋ค. ์ฌ๋ฌ ๋ช ๋ช ๋ ๊ตฌ์ฑ ์์๋ฅผ ๋ด๋ณด๋ ๋๋ค. ๋๋ ์ด๋ฌํ ๋ช ๋ช ๋ ๋ด๋ณด๋ด๊ธฐ ์ค ๋ช ๊ฐ๋ง ์กฐ๋กฑํ๊ณ ๋๋จธ์ง๋ ์ง์ง๋ก ๋จ๊ฒจ๋๊ณ ์ถ์์ต๋๋ค.
๋ฐ๋ผ์ __mocks__/shared-components.js
์๋ ๋ค์์ด ์์ต๋๋ค.
const original = require.requireActual('shared-components');
module.exports = {
...original,
moduleNameToOverride: jest.fn().mockImplementation(() => {
return 'whatever';
}),
}
์ ๊ฒฝ์ฐ์๋ ๊ตฌํ์ ์คํ ์์ํ์ต๋๋ค. ์ด๊ฒ์ด ๋ฏธ๋์ ๋๊ตฐ๊ฐ๋ฅผ ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
๋๋ ์ต๊ทผ์ ๊ฐ์ ๋ฌธ์ ์ ์ง๋ฉดํ์ต๋๋ค. ์ด ์ค๋ ๋์ ๋ํ๋ ๋ ์ ์ดํดํ๋ ๋ฐ ๋์์ด ๋์์ผ๋ฉฐ ์ฌ๊ธฐ์์ ๋ด ์ฐ๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ์์ฝํ์ต๋๋ค. https://medium.com/@DavideRama/mock -spy-exported-functions-within-a-single- ๋ชจ๋-์ธ-์ ์คํธ-cdf2b61af642
@qwertie ์ ์๋ฃจ์ ์์ ์๊ฐ์
mockGet = jest.fn()
jest.mock('my-module', () => ({
...jest.requireActual('my-module'),
get: mockGet
}))
React.lazy
์ ์๋ํ๋ @MajorBreakfast ?
const mockLazy = jest.fn();
jest.mock('React', () => ({
...jest.requireActual('React'),
lazy: mockLazy
}));
๋๋ ์ฌ์ ํ ReferenceError: React is not defined
๋ฐ์ต๋๋ค.
๋
๋ฆฝ ๋ด๋ณด๋ด๊ธฐ ๊ธฐ๋ฅ ๋ชจ๋์ ์กฐ๋กฑํ๋ ค๋ฉด:
ํ์ผ์์ ๊ธฐ๋ณธ ๋ด๋ณด๋ด๊ธฐ์ ๋ชจ๋ ๊ฐ๋ณ ๊ธฐ๋ฅ ๋ถ๋ถ์ ๋ด๋ณด๋
๋๋ค.
์์:
dataDao.js
ํจ์ getData()
ํจ์ setData()
ํจ์ ์ญ์ ๋ฐ์ดํฐ()
{getData, setData, deleteData} ๋ด๋ณด๋ด๊ธฐ
์ด์ ๊ธฐ๋ณธ ์ด๋ฆ ์ง์ ์ผ๋ก ํ์ผ์ ๋ชจ๋ ๊ธฐ๋ฅ์ jest ํ ์คํธ๋ก ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
dataDao.spec.js
* '../dataDao'์์ dataDao๋ก ๊ฐ์ ธ์ค๊ธฐ;
// ๊ฐ์ ธ์ฌ ๋ ํ ๋น๋ ๊ธฐ๋ณธ ์ด๋ฆ์ ์ฐธ์กฐํ๋ ๋ชจ๋์ ๊ฐ์ํฉ๋๋ค.
jest.spyOn(dataDao, 'getData')
jest.spyOn(dataDao, 'setData')
jest.spyOn(dataDao, '๋ฐ์ดํฐ ์ญ์ ')
@vchinthakunta , ์๋ํ ์ ์์ง๋ง ๋ด๋ณด๋ด๊ธฐ/๊ฐ์ ธ์ค๊ธฐ ๊ตฌ๋ฌธ์ ์ฃผ์ ๋ชฉ์ ์ ์๋ฐํ๋ ๊ฒ ๊ฐ์ต๋๋ค. ๋ค๋ฅธ ๋ชจ๋์ ๋ ์ด์ ๋ค์์ ํตํด ํน์ ๋ฉ์๋ ๋๋ ๋ฐ์ดํฐ ํ๋๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
import { justThisThing } from 'someModule';
๋ด๊ฐ ๋ญ๊ฐ๋ฅผ ๋์น๊ณ ์์ต๋๊น?
React.lazy
์ ์๋ํ๋ @MajorBreakfast ?const mockLazy = jest.fn(); jest.mock('React', () => ({ ...jest.requireActual('React'), lazy: mockLazy }));
๋๋ ์ฌ์ ํ
ReferenceError: React is not defined
๋ฐ์ต๋๋ค.
๊ฐ์ ธ์ค๊ธฐ๋ฅผ ์ฐธ์กฐํ๊ธฐ ๋๋ฌธ์ 'React'๋ ์ฌ๊ธฐ์์ ์๋ฌธ์์ฌ์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๊น?
jest.mock('react'...)
์ฌ๊ธฐ์์ ๋ณธ ๋ค๋ฅธ ์๋ฃจ์
๋ณด๋ค ๊ฐ๋จํ ๋ค์์ ์ฌ์ฉํ์ฌ ์ฝ๋๋ฅผ ์๋์์ผฐ์ต๋๋ค. require
ํ๊ฑฐ๋ default
๋ด๋ณด๋ด๊ธฐ๋ฅผ ์ค์ ํ ํ์๊ฐ ์์ต๋๋ค.
๋์ฐ๋ฏธ/๋ด๋น๊ฒ์ด์ .js
export const initHeader = () => {
// initialise header
...
}
...
export const anotherHelperFunction = () => {
// do stuff
...
}
Navigation.js๋ฅผ ์ฌ์ฉํ๋ ๊ตฌ์ฑ ์์
import { initHeader } from '../helpers/navigation';
jest.mock('../helpers/navigation');
...
describe('Test component', () => {
it('should reinitialise header', () => {
const mockHeaderInit = jest.fn();
initHeader.mockImplementation(mockHeaderInit);
const component = mountComponent(mockProps);
component.simulate('click');
expect(mockHeaderInit).toBeCalled();
}
}
mockGet = jest.fn() jest.mock('my-module', () => ({ ...jest.requireActual('my-module'), get: mockGet }))
ReferenceError: mockGet is not defined
4 | const mockGet = jest.fn();
5 | jest.mock('./browserStorage', () => ({
6 | ...jest.requireActual('./browserStorage'),
> 7 | get: mockGet,
| ^
8 | }));
jest.mock
๊ฐ ํธ์ด์คํธ๋ ๊ฒฝ์ฐ doMock
ํ๊ฑฐ๋
jest.mock('./browserStorage', () => ({
...jest.requireActual('./browserStorage'),
get: jest.fn(),
}));
const {get: mockGet} = require('./browserStorage');
์ด๊ฒ์ ์ถฉ๋ถํ ์ผ๋ฐ์ ์ธ ๋ฌธ์ ์ธ ๊ฒ ๊ฐ์ต๋๋ค. jest ๋ฌธ์์ ์ด๊ฒ์ ๊ดํ ๊ฒ์ด ์์ต๋๊น?
๊ทธ๋ ๋ค๋ฉด ๋์ผํ ๋ชจ๋ ๋ด ์์ ํจ์๋ฅผ ์กฐ๋กฑํ๋ ์๋ฃจ์ ์ด ์์ต๋๊น?
๋ค์ ๋ฐฉ๋ฒ์ด ์ ์๊ฒ ํจ๊ณผ์ ์ด์์ต๋๋ค. ํธ๋ฆญ์ ํ
์คํธ๊ฐ ๋๋ ๋ ๋ชจ์ ๊ธฐ๋ฅ์ ๋ค์ ์ฌ์ค์ ํ๋ ๊ฒ์
๋๋ค.
์ด ์์ ๋ jsonwebtoken ๋ชจ๋์ verify ๊ธฐ๋ฅ์ ๋ชจ์ํฉ๋๋ค.
test('perform my test', async () => {
// save the real jwt.verify function
const verify = jwt.verify
// mock it.
jwt.verify = jest.fn().mockReturnValue({ sub: 0 })
// do the test
...
// set the real function back.
jwt.verify = verify
})
๋ค์ ๋ฐฉ๋ฒ์ด ์ ์๊ฒ ํจ๊ณผ์ ์ด์์ต๋๋ค. ํธ๋ฆญ์ ํ ์คํธ๊ฐ ๋๋ ๋ ๋ชจ์ ๊ธฐ๋ฅ์ ๋ค์ ์ฌ์ค์ ํ๋ ๊ฒ์ ๋๋ค.
์ด ์์ ๋ jsonwebtoken ๋ชจ๋์ verify ๊ธฐ๋ฅ์ ๋ชจ์ํฉ๋๋ค.test('perform my test', async () => { // save the real jwt.verify function const verify = jwt.verify // mock it. jwt.verify = jest.fn().mockReturnValue({ sub: 0 }) // do the test ... // set the real function back. jwt.verify = verify })
const verify
์ด๋์์ ์ฌ์ฉํ๊ณ ์์ต๋๊น? ์ด์จ๋ ์ด๊ฒ์ ์กฐ๋กฑ๋ ํจ์๊ฐ ๋ด๋ณด๋ธ const ํจ์๊ฐ ์๋ ๊ฒฝ์ฐ์๋ง ์๋ํฉ๋๋ค.
๊ทธ๋ ๋ค๋ฉด _๋์ผํ ๋ชจ๋ ๋ด์์_ ํจ์๋ฅผ ์กฐ๋กฑํ๋ ์๋ฃจ์ ์ด ์์ต๋๊น?
๋ง์ ๊ฒ์ ํ์ ์ด ๋ฌธ์ ์ ๋ํ ํด๊ฒฐ์ฑ ์ ํจ์์ ๋ชจ์ ๊ฐ์ฒด๊ฐ ์ฐธ์กฐํ ์ ์๋ ๋จ์ผ ๊ฐ์ฒด์ ๋ด๋ณด๋ด๊ธฐ๋ฅผ ์ ์ฅํ๋ ๊ฒ์ ๋๋ค. ์๋ ๊ธฐ์ฌ๋ค์ ๋ชจ๋ ๊ฐ์ ํฉ์์ ์ด๋ฅด๋ ์ต๋๋ค.
https://github.com/facebook/jest/issues/936#issuecomment -438975674
https://medium.com/@qjli/how -to-mock-specific-module-function-in-jest-715e39a391f4
https://luetkemj.github.io/170421/mocking-modules-in-jest
์๋ฌด๋ ๋ค๋ฅธ ์๋ฃจ์ ์ ์ธ๊ธํ์ง ์์๋ค๋ ์ฌ์ค์ด ๋๋์ต๋๋ค. ์ด ์๋ฃจ์ ์ด ์ฝ๋์ ์ ํฉํ๋ค๋ฉด ์ด๋ฌํ ๋ชจ๋ ๋ฌธ์ ๋ฅผ ์์ ํ ์ ๊ฑฐํ๊ณ ์ํ๋ ์ฝ๋๋ฅผ ๋งค์ฐ ๊ฐ๋จํ๊ฒ ํ ์คํธํ ์ ์์ต๋๋ค.
_๋ชจ์ํ๋ ค๋ ๋จ์ผ ํจ์๋ฅผ ์์ฒด ๋ชจ๋๋ก ์ด๋ํฉ๋๋ค._
์ง์งํ๊ฒ. ๋ชจ๋์ด ๋ค๋ฅธ ๋ด๋ถ ๋ถํ๊ณผ ๋ณ๋๋ก ๋ด๋ถ ๋ถํ์ ํ ์คํธํด์ผ ํ๋ ๋ฐฉ์์ผ๋ก ์์ฑ๋ ๊ฒฝ์ฐ ํด๋์ค๊ฐ ๋จ์ผ ์ฑ ์ ์์น์ ์๋ฐํ๋ ๊ฒ์ด ๊ฑฐ์ ํ์คํฉ๋๋ค(์, ์ค์ ํด๋์ค๋ ์๋์ง๋ง ๋ชจ๋์ ํด๋์ค์ฒ๋ผ ์๋ํฉ๋๋ค. ๋ชจ๋์ ์ฝ๋์ ๋จ์ ์ปจํ ์ด๋์). ๊ทธ ๋นจํ์ ๋๋๊ณ ๋ถ, ๋น์ ์ ์๊ตฌ๋ฅผ ์กฐ๋กฑ ํ ์ ์์ต๋๋ค.
๋ชจ์ ํจ์๊ฐ ์ฌ๋ฌ ๊ฐ์ธ ์ํ์ ์์กดํ๋ ๊ฒฝ์ฐ ์ฌ์ ํ ๋ชจ๋์ ์ด๋ป๊ฒ๋ ๋ถํ ํ์ง ์๋ ์ข์ ์ด์ ๊ฐ ์๋๋๋ค. ๋ง์ ๋ด๋ถ ์ํ์ ์์กดํ๋ค๋ ๋ฐ๋ก ๊ทธ ์ฌ์ค์ ๋์๊ฒ ๋ชจ๋์ ๋ฌธ์ ๊ฐ ๋ช ํํ๊ฒ ๊ณ ๋ ค๋์ง ์์์์ ์๋ฏธํฉ๋๋ค. ์๋ง๋ ์ด๋ค ์ข ๋ฅ์ ๋ฐ์ดํฐ ํด๋์ค ๋๋ DTO๋ฅผ ๋ํ๋ด๋ ๋ถํ ํ ์ธ ๋ฒ์งธ ๋ชจ๋์ด ์์ ์ ์์ผ๋ฉฐ ์ธ์๋ก ์ ๋ฌํ ์ ์์ต๋๋ค.
๋ํ ๋น๊ณต๊ฐ๊ฐ ๋ ํ ์คํธ๋ฅผ ์ํด ๊ธฐ๋ฅ์ ๋ด๋ณด๋ด๊ณ ์์ต๋๊น? ์ธ๋ถ ์ฝ๋๊ฐ ์กฐ๋กฑ๋ ํจ์๋ฅผ ์ง์ ํธ์ถํ์ง๋ง ์์ฒด์ ์ผ๋ก ํธ์ถํด์ผ ํ๋ ๋ค๋ฅธ ํจ์๋ ํธ์ถํ๋ ์ด์ ๋ ๋ฌด์์ ๋๊น? ๋๋ ์ฌ๊ธฐ์์ ์ด๋ค ์ข ๋ฅ์ ์ฐ๊ฒฐ ๋๊น์ด ์ผ์ด๋๊ณ ์๋ค๊ณ ํ์ ํฉ๋๋ค. ์กฐ๋กฑ๋ ํจ์๋ ๊ทธ๋๋ก ์์ด์ผ ํ์ง๋ง ๋ชจ๋ ํจ์๋ ๋ฐ์ผ๋ก ๋ถํ ๋์ด์ผ ํ๊ณ ๋ฐ์ ์ ๊ฑฐ๋์ด ๋ค๋ฅธ ๋ชจ๋๋ก ์ด๋ํด์ผ ํฉ๋๋ค. ๋น์ ์ ์์ด๋์ด๋ฅผ ์ป์.
ํ ์คํธ๊ฐ ๋งค์ฐ ์ด๋ ค์์ง๋ฉด ๊ฑฐ์ ํญ์ ๋ฆฌํฉํ ๋ง์ด ํ์ํ๋ค๋ ์ ํธ์ ๋๋ค...
ํ ์คํธ๊ฐ ํ์ํ์ง ์์ต๋๋ค.
const tomfoolery = require('tomfoolery'); // no longer required
์ด ์ค๋ ๋๋ฅผ ์ฝ๊ณ ์ ์๋ ์๋ฃจ์ ์ ํ ์คํธํ ํ์๋ ์ฌ์ ํ ์๋ํ์ง ์์ต๋๋ค. ๋ด๊ฐ ์ฝ์ ๊ฒ์์ ์ผ๋ถ ์ฌ๋๋ค์์ด ์์ ์ ์ํํ์ง๋ง ์ด๋ป๊ฒ ์์๋ผ ์ ์์ต๋๋ค.
๋๊ตฐ๊ฐ ํ ์คํธ๋ฅผ ํต๊ณผํ๊ธฐ ์ํด ๋ค์ ์์ ์ ์ถ๊ฐํด์ผ ํ๋ ์ฝ๋๋ฅผ ๋งํด ์ค ์ ์์ต๋๊น?
// a.js
export const foo = () => 'foo-' + bar()
export const bar = () => 'bar'
// a.test.js
import {
foo,
bar
} from './a'
describe('foo', () => {
it('should return foo-MOCKED_BAR', () => {
expect(foo()).toBe('foo-MOCKED_BAR')
})
it('should have the mocked implementation of bar', () => {
expect(bar()).toBe('MOCKED_BAR')
})
})
describe('bar', () => {
it('should have the original implementation of bar', () => {
expect(bar()).toBe('bar')
})
})
describe('foo and bar together', () => {
it('should have the original implementation of both', () => {
expect(foo()).toBe('foo-bar')
})
})
๊ฐ์ฌ ํด์!
lodash.random๊ณผ ๊ฐ์ ๋จ์ผ lodash ๋ฉ์๋๋ง ์กฐ๋กฑํ๊ณ ์ถ์๊ณ ๋ค์๊ณผ ๊ฐ์ด ์ฝ๊ฒ ํ ์ ์์์ต๋๋ค.
๋ชจ๋.js
const lodash = require('lodash');
module.exports = function() {
return lodash.random();
}
test.js
const lodash = require('lodash');
const module = require('./module.js);
it('mocks lodash', () => {
jest.spyOn(lodash, 'random').mockImplementationOnce(() => {
return 2;
});
expect(module()).toEqual(2)
});
๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค :)
typescript๋ก ์์
ํ๋ ์ฐ๋ฆฌ ํ์์ ํจ๊ณผ๊ฐ ์์๋ ๊ฒ์ ํจ์๋ฅผ ์ง์ ๋ด๋ณด๋ด๋ ๋์ ๋ด๋ณด๋ด๋ const
๋ฅผ ๋ง๋๋ ๊ฒ์ด์์ต๋๋ค.
์๋ ์ํจ:
export function doSomething(a, b) {}
์ผํ๊ณ ์๋:
export const doSomething = function (a, b) {}
@arbielsk ๊ฐ ํ๋ ํ๋๋ฐ ์๋ํฉ๋๋ค! ํ์ง๋ง ๋ ์ข ๋ฅ์ ์์ถ์ ์ฐจ์ด์ ์ด ๋ฌด์์ธ์ง ๋ชจ๋ฅด๊ฒ ์ต๋๋ค ...
@dgrcode ๊ทํ์ ์์ ์ ๋ํ ์๋ฃจ์
์ ์ฐพ์ ์ ์ด ์์ต๋๊น? ๋ด๊ฐ ๋งํ ์ ์๋ ํ, ๋น์ ์ด ํ๋ ค๋ ๊ฒ์ Jest๋ฅผ ํตํ ์กฐ๋กฑ์ผ๋ก ์ง์๋์ง ์์ต๋๋ค. ํนํ, ์กฐ๋กฑ์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋์ ์ธ๋ถ ๋ณด๊ธฐ๊ฐ ์กฐ๋กฑ๋ ๋ฉ์๋๋ฅผ ๋ณผ ์ ์๋๋ก ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ๋ค์ ์ฐ๊ฒฐํ๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐ ํฉ๋๋ค. ๊ทธ๋ฌ๋, ๊ทํ์ ์์ ์์, foo
๋ฐ bar
๊ฐ์ ๋ชจ๋์ ์๊ณ ๊ทธ๋์ foo
์์๋ณด๊ธฐ bar
๋ฐ์ผ๋ก ์กฐ๋กฑ ํ ์ ์์ต๋๋ค.
๊ทํ์ ์ต์
์ ๋ค์ ์ค ํ๋๋ผ๊ณ ์๊ฐํฉ๋๋ค.
1) foo
๊ฐ bar
๋ฅผ ํฌํจํ๋ ๋ชจ๋์ ๊ฐ์ ธ์ค๋๋ก ์ฝ๋๋ฅผ ์ฌ๊ตฌ์ฑํฉ๋๋ค.
2) babel-plugin-rewire ์ฌ์ฉ
์ ๊ฐ ์คํดํ๊ณ ์๋ ๋ถ๋ถ์ด ์๋ค๋ฉด ์ ์ ํด์ฃผ์ธ์!
์ฝ๊ฐ ๋ค๋ฅธ ์๊ตฌ ์ฌํญ์ด ์์์ต๋๋ค. ํ๋์ ํจ์์ ๋ํด ์ ์ฒด ๋ชจ๋ _except_์ ๋ชจ์ํ๊ณ ์ถ์์ต๋๋ค. @MajorBreakfast ์ ์๋ฃจ์ ์ ์์์ ์ผ๋ก ์ฌ์ฉํ์ฌ ๋ค์์ ์๊ฐํด ๋์ต๋๋ค.
jest.mock('my-module', () => ({
...jest.genMockFromModule('my-module'),
myFunction: jest.requireActual('my-module').myFunction
}))
@dgrcode ๊ทํ์ ์์ ์ ๋ํ ์๋ฃจ์ ์ ์ฐพ์ ์ ์ด ์์ต๋๊น? ๋ด๊ฐ ๋งํ ์ ์๋ ํ, ๋น์ ์ด ํ๋ ค๋ ๊ฒ์ Jest๋ฅผ ํตํ ์กฐ๋กฑ์ผ๋ก ์ง์๋์ง ์์ต๋๋ค. ํนํ, ๋๋ ์กฐ๋กฑํ๋ ๊ฒ์ด ๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋์ ์ธ๋ถ ๋ณด๊ธฐ๊ฐ ์กฐ๋กฑ๋ ๋ฉ์๋๋ฅผ ๋ณผ ์ ์๋๋ก ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ๋ค์ ์ฐ๊ฒฐํ๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค. ๊ทธ๋ฌ๋, ๊ทํ์ ์์ ์์,
foo
๋ฐbar
๊ฐ์ ๋ชจ๋์ ์๊ณ ๊ทธ๋์foo
์์๋ณด๊ธฐbar
๋ฐ์ผ๋ก ์กฐ๋กฑ ํ ์ ์์ต๋๋ค.๊ทํ์ ์ต์ ์ ๋ค์ ์ค ํ๋๋ผ๊ณ ์๊ฐํฉ๋๋ค.
foo
๊ฐbar
๋ฅผ ํฌํจํ๋ ๋ชจ๋์ ๊ฐ์ ธ์ค๋๋ก ์ฝ๋๋ฅผ ์ฌ๊ตฌ์ฑํ์ญ์์ค.- babel-plugin-rewire ์ฌ์ฉ
์ ๊ฐ ์คํดํ๊ณ ์๋ ๋ถ๋ถ์ด ์๋ค๋ฉด ์ ์ ํด์ฃผ์ธ์!
์ค์ ๋ก ๋ด ๊ฒฝ์ฐ๊ฐ ๊ทธ๋ฌ๋ค.
๋ชจ๋์ ์กฐ๋กฑํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๋ด ์ดํด๊ฐ ์๋ง์ด์์ต๋๋ค ๐คฆโโ
์๋
2๊ฐ์ ํจ์๊ฐ ๊ฐ์ ๋ชจ๋์ ์๊ณ ์๋ก๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ
foo
์ด bar
ํธ์ถํ๋ ๊ฒฝ์ฐ
function bar() {
return 'some-result'
}
function foo(){
return bar() // <-- the function I want to mock
}
bar
๋ฅผ ์ ํ์ผ์ ๋ฃ์ต๋๋ค(์กฐ๋กฑํ ํ์ผ).
์ ํ์ผ์์ bar
๋ฉ์๋๋ฅผ ์ด๋ํ์ผ๋ฉฐ ์ด์ ์์ ๋ง์ ์์ ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
@yoni-abtech ๋๋ถ์ ์ดํดํ๊ฒ ๋์์ต๋๋ค ๐คฃ
์ด ์ ์ฒด ์ค๋ ๋๋ฅผ ์ฝ๊ณ ๋ฐ๋ณตํด์ ํ ์คํธํ ํ ๋ด๊ฐ ๋ณด๋ ๋ฐฉ์์๋ 3๊ฐ์ง ์ต์ ์ด ์์ต๋๋ค....
const
์ฌ์ฉํ์ฌ ๋ชจ๋ ํจ์ ์ ์ธ์ด๋ฅผ ์ํด์๋ ์ ์ธ ๋ณด๋ค ํจ์ ํํ์ ์ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ๋คํํ func-style
eslint ๊ท์น์ด ๋์์ด ๋์์ต๋๋ค.
export const
์ฌ์ฉํ๋ฉด _same_ ๋ชจ๋ ๋ด์์ ๋ค๋ฅธ ํจ์์์ ์ฌ์ฉํ๋ spyOn
ํจ์๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
// hello.js
export const message = () => {
return 'Hello world';
}
export const foo = () => {
return message();
}
// hello.test.js
import * as testModule from './hello.js';
describe('test spyon with function expressions', function () {
afterAll(() => {
jest.restoreAllMocks();
});
it('should NOT mock message in foo', function () {
const actual = testModule.foo();
expect(actual).toBe('Hello world');
});
it('should mock message in foo', function () {
jest.spyOn(testModule, 'message').mockReturnValue('my message');
const actual = testModule.foo();
expect(actual).toBe('my message');
expect(testModule.message).toHaveBeenCalledTimes(1);
});
});
rewire
babel ํ๋ฌ๊ทธ์ธ ์ฌ์ฉํจ์ ํํ์์ ์ฌ์ฉ์ ์๋ฌดํํ์ง ์์ผ๋ ค๋ฉด(์: const
) ์ด๊ฒ์ด ์ข์ ์ ๊ทผ ๋ฐฉ์์ด ๋ ์ ์์ต๋๋ค.
์ด๋ฅผ ํตํด ๋์ผํ ๋ชจ๋์์ ํจ์๋ฅผ _rewire_ (์ผ๋ช
๋ชจ์)ํ ์ ์์ต๋๋ค. ์ฝ๋๊ฐ ์๋์ ๊ฐ์ด ๋ณด์ผ ๊ฒ์ด๋ผ๊ณ ์์ํ ์ ์์ง๋ง ํ
์คํธํ์ง๋ ์์์ต๋๋ค. ๋ํ ๊ทธ๋ค์ ๋ฌธ์ ์์ ๋ชจ๋ ๐์์ ๋ด๋ณด๋ด์ง ์์ ๋์ผํ ๋ชจ๋์ ํจ์๋ฅผ ๋ค์ ์ฐ๊ฒฐํ ์ ์๋ ๊ฒ์ฒ๋ผ ๋ณด์
๋๋ค. message
ํจ์๋ฅผ ๋ด๋ณด๋ด์ง ์๊ณ ์๋ ์๋ฅผ ์์ํด ๋ณด์ธ์.
์์:
// hello.js
export function message() {
return 'Hello world';
}
export function foo() {
return message();
}
// hello.test.js
import * as testModule from './hello.js';
describe('test rewire api', function() {
it('should NOT mock message in foo', function () {
const actual = testModule.foo();
expect(actual).toBe('Hello world');
});
it('should mock message in foo', function () {
testModule.__RewireAPI__.__Rewire__('message', jest.fn().mockReturnValue('my message'));
const actual = testModule.foo();
expect(actual).toBe('my message');
expect(testModule.message).toHaveBeenCalledTimes(1);
testModule.__RewireAPI__.__ResetDependency__('message');
});
});
์์ ๋ ๋ฌธ์ ๋ฅผ ์ฐธ์กฐํ์ญ์์ค.
์ฐธ๊ณ : babel ๋ณํ์ด ํ์ํฉ๋๋ค.
์ด ์ต์
์ ๊ฐ์ฅ ๋ถ๋ฆฌํ์ง๋ง ์ผ๋ฐ์ ์ธ mock
๊ธฐ๋ฅ์์๋ ๋ถ๋ช
ํ ์ ์๋ํฉ๋๋ค.
์ถ์ : ์ด ํธ๋ ๋๋ ๋งค์ฐ ๊ณ๋ชฝ์ ์ด๊ณ ์ข ์ข ์ฌ๋ฏธ์์์ง๋ง, ์ด ์๋์์ค๊ฐ ๋ค๋ฅธ ์ฌ๋๋ค์ด ์ ์ฒด ์ค๋ ๋๋ฅผ ์ฝ์ ํ์์ฑ์ ์ํํ๊ธฐ๋ฅผ ๋ฐ๋๋๋ค. โ๏ธ
@nickofthyme ๊ฐ์ฌ
@nickofthyme ๊ทํ์ option 1
๋ ๋ด ์ฑ์์ ์คํจํ๊ณ create react app
:
FAIL src/hello.test.js
test spyon with function expressions
โ should NOT mock message in foo (3ms)
โ should mock message in foo (6ms)
โ test spyon with function expressions โบ should mock message in foo
expect(received).toBe(expected) // Object.is equality
Expected: "my message"
Received: "Hello world"
17 | const actual = testModule.foo();
18 |
> 19 | expect(actual).toBe("my message");
| ^
20 | expect(testModule.message).toHaveBeenCalledTimes(1);
21 | });
22 | });
at Object.toBe (src/hello.test.js:19:20)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 1.848s
Ran all test suites related to changed files.
@danielhusar ๋น์ ์ด ๋ง๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ฃ์กํฉ๋๋ค. CRA๋ก ์ด๊ฒ์ ํ ์คํธํ์ด์ผ ํ์ต๋๋ค.
์ฌ๊ธฐ ์์ ์๋ํ๋๋ก ์ต์
1์ ์ค๋ช
๋ ์กฐ๋กฑ์ด yarn test:hello
์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ์ฌ ํ
์คํธํฉ๋๋ค.
> yarn test:hello
yarn run v1.16.0
$ jest --config=jest.config.js -t=hello --verbose
PASS src/hello/hello.test.ts
test hello
โ should NOT mock message in foo (3ms)
โ should mock message in foo (1ms)
Test Suites: 1 skipped, 1 passed, 1 of 2 total
Tests: 1 skipped, 2 passed, 3 total
Snapshots: 0 total
Time: 1.392s
Ran all test suites with tests matching "hello".
โจ Done in 2.36s.
๊ทธ๊ฒ์ ์ฌ์ฉ์ ์ ์ํ์ฌ ํ์ jest.config.js
์ฌ์ฉํ์ฌ ํ์ผ ts-jest
ํ๊ณ ์ ํ jest --config=./jest.config.js
์ง์ ํ์ง ํตํด react-scripts
. jest๊ฐ react-scripts
์ ์ด๋ป๊ฒ ๊ตฌ์ฑ๋์ด ์๋์ง ์ ๋ชจ๋ฅด๊ฒ ์ง๋ง ์ด๋ป๊ฒ๋ ๊ตฌ์ฑ์ ์
๋ฐ์ดํธํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์์ ์ ์๋ค๊ณ ์๊ฐํฉ๋๋ค.
์ด ์์ ์ *.css
๋ฐ *.svg
ํ์ผ์ ๋ํ ๋ณํ์ ์ ๊ฑฐํ๋ฏ๋ก App.tsx
์ค๋ฅ๋ฅผ ๋ฌด์ํ์ญ์์ค.
์์
์ ์ํํ๊ธฐ ์ํด ์ํํด์ผ ํ๋ ํน์ ์์
์ด ์์ต๋๊น?
๋๋ ๊ฝค ํ์ค์ ์ธ ์ค์ (ts ์์ด)์ ๊ฐ์ง๊ณ ์๊ณ ๊ทธ๊ฒ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ํ์ง ์๋๋ค๊ณ ๋งํ ๊ฒ์
๋๋ค.
์ค๋ ๋ฐค์ ์กฐ๊ธ ๋ ์ดํด๋ณด๊ณ ๊ทธ๊ฒ์ด ๊ฐ๋ฅํ์ง ํ์ธํ๊ฒ ์ต๋๋ค.
@danielhusar ์ด์ ฏ๋ฐค์ ์กฐ์ฌํ ๊ฒฐ๊ณผ ์ฆ์ ์ฌ์ฉ ๊ฐ๋ฅํ ์๋ฃจ์
์ ์ป์ ์ ์์์ต๋๋ค. ํต์ฌ์ CRA๊ฐ package.json#jest
์์ ์ฌ์ ์ ํ ์ ์๋ jest config transformer
์
๋๋ค. js ๋ฐ ts ํ์ผ์ babel-jest
์ฌ์ฉํ์ฌ ๋ณํ๋์ง๋ง react-scripts
๋ .babelrc
๊ตฌ์ฑ ํ์ผ์ ์ฌ์ฉํ๊ณ test
์ค์ ํ ํ๊ฒฝ ์ ์ค์ ํ๋ ๊ฒ์ ์ฐจ๋จ ํฉ๋๋ค react-scripts test
์ฌ๊ธฐ .
๋ ๊น์ด ํ๊ณ ๋ค๊ณ ์ถ์ง๋ง ์ง๊ธ์ ์๊ฐ์ด ์์ต๋๋ค.
ํ ์ฌ์ ํ ์๋ํ๊ฒ ๋ง๋๋ ๋ฐ ์ด๋ ค์์ ๊ฒช๊ณ ์์ต๋๋ค(CRA๊ฐ ์๋ ๋ด ์ฌ์ฉ์ ์ง์ ์ค์ ์์).
(jest์ babel-jest ๋ชจ๋์ ์ต์ ๋ฒ์ )
์ด๊ฒ์ ๋ด ๋๋ด ๊ตฌ์ฑ์ ๋๋ค.
module.exports = {
name: 'my-app',
testURL: 'http://localhost/',
setupFiles: ['<rootDir>/setup-jest.js'],
setupFilesAfterEnv: ['<rootDir>/setup-test.js'],
testMatch: ['**/__tests__/**/*.test.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
testEnvironment: 'jest-environment-jsdom-fifteen',
snapshotSerializers: ['enzyme-to-json/serializer'],
globals: {
ENV: 'test',
},
transform: {
'^.+\\.[t|j]sx?$': 'babel-jest',
},
};
๊ทธ๋ฆฌ๊ณ ๋ด babelrc :
{
"presets": [
"@babel/react",
["@babel/env", {
"corejs": "3",
"useBuiltIns": "entry",
"loose": true,
"exclude": [
"es.string.split"
]
}],
"@babel/flow"
],
"plugins": [
"array-includes",
"lodash",
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-class-properties",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread",
"@babel/plugin-proposal-optional-chaining"
],
"env": {
"test": {
"plugins": ["dynamic-import-node"]
}
}
}
์ต์
1๋ก ์ด๋ ค์์ ๊ฒช๊ณ ์๋ ์ฌ๋์๊ฒ๋ () => (expression)
ํจ์ ๋์ () => { return expression }
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
๋ค์๊ณผ ๊ฐ์ด ์์ ํ์ฌ ์๋ํ๋๋ก ์ต์ 1์ ์ป์์ต๋๋ค.
import * as test from './test';
export const message = () => {
return 'Hello world';
}
export const foo = () => {
return test.message();
}
์์์ง๋ ์์ง๋ง ์๋ํด์ผ ํฉ๋๋ค.
@nickofthyme , ์ต์ 1 ์ ๋น์ ์ด ๊ฐ์ง๊ณ ์๋ ๊ฒ์ฒ๋ผ ์ ํํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝํ๋ฉด
const foo = () => {}
export { foo }
๊ทธ๋ฐ ๋ค์ ๋ถ์์ง๋๋ค. ์๋ง๋ ์ ๊ฐ์ฒด ๋ฆฌํฐ๋ด์ ๋ง๋ค๊ณ ๋ด๋ณด๋ด๊ธฐ ๋๋ฌธ์ผ ๊ฒ์ ๋๋ค.
ํฅ๋ฏธ๋ก์ด ๊ด์ฐฐ. @maletor ๊ฐ์ฌ
Jest๋ ๋ฌธ์์ ๋ชจ๋์ ๋ถ๋ถ์ ์ผ๋ก ๋ชจ์ํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๋งค์ฐ ๊ฐ๋จํ๊ณ ๋ช
ํํ ์๊ฐ ์์ต๋๋ค. ์ด๊ฒ์ ES import ๋ฐ Node require ๋ฌธ ๋ชจ๋์์ ์๋ํฉ๋๋ค.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename
@johncmunson ์ข์ ์ง์ ์
๋๋ค. ๊ทธ๋ฌ๋ ๋ชจ๋ ๋ชจ์์ ๋ํด ๋ณด์ฌ์ค ์ด ์์ ๋ jest.mock
_once_ ์คํ
์์ ์๋ฅผ ๋ค๋ฉด... foo
์ bar
์ฌ์ด์ ๋ชจ๋์ ์ด๋ป๊ฒ ๋ค๋ฅด๊ฒ ์กฐ๋กฑํ๊ณ ์ถ์์ง ๋ณด์ฌ์ฃผ๊ธฐ ์ํด bar
๋ฅผ ์ถ๊ฐํ์ต๋๋ค.
export const message = (): string => {
return 'Hello world';
}
export const foo = (): string => {
return message();
}
export const bar = (): (() => string) => {
return foo;
}
์ฌ์ฉ jest.mock
๊ฐ์ง jest.requireActual
๋๋ ์ด๋ฐ ์์ผ๋ก ๋ญ๊ฐ๋ฅผ ๊ฐ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
import * as mockTestModule from './hello';
jest.mock('./hello');
const actualTestModule = jest.requireActual('./hello');
describe('test hello', function () {
afterAll(() => {
jest.restoreAllMocks();
});
// first test doesn't depend on any other method of the module so no mocks
it('should NOT mock message in foo', function () {
const actual = actualTestModule.foo();
expect(actual).toBe('Hello world');
});
// the second I want to mock message in foo
it('should mock message in foo', function () {
jest.spyOn(mockTestModule, 'message').mockReturnValue('my message');
const actual = actualTestModule.foo();
expect(actual).toBe('my message'); // fails
expect(mockTestModule.message).toHaveBeenCalledTimes(1); // never called
});
it('should mock foo in bar', function () {
jest.spyOn(mockTestModule, 'foo').mockReturnValue('my message');
const actual = actualTestModule.bar();
expect(actual()).toBe('my message'); // fails
expect(mockTestModule.message).toHaveBeenCalledTimes(1); // never called
});
});
jest.doMock
๋ฐ๋ก ์กฐ๋กฑ์ ์๋ํ์ง๋ง ์ฌ์ ํ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ์ป์์ต๋๋ค.
์ฝ๋๋ฅผ ๋ณด๋ ค๋ฉด ํด๋ฆญํ์ธ์.
```t
* import *๋ฅผ './hello'์์ testModule๋ก;
describe('ํ
์คํธ ์๋
ํ์ธ์', ํจ์() {
AfterAll(() => {
jest.restoreAllMocks();
});
it('should NOT mock message in foo', function () {
const actual = testModule.foo();
expect(actual).toBe('Hello world');
});
it('should mock message in foo', function () {
jest.doMock('./hello', () => {
// Require the original module to not be mocked...
const originalModule = jest.requireActual('./hello');
return {
...originalModule,
message: jest.fn().mockReturnValue('my message'),
};
});
const actual = testModule.foo();
expect(actual).toBe('my message'); // fails
expect(testModule.message).toHaveBeenCalledTimes(1); // never called
});
it('should mock foo in bar', function () {
jest.doMock('./hello', () => {
// Require the original module to not be mocked...
const originalModule = jest.requireActual('./hello');
return {
...originalModule,
foo: jest.fn().mockReturnValue('my message'),
};
});
const actual = testModule.bar()();
expect(actual).toBe('my message'); // fails
expect(testModule.foo).toHaveBeenCalledTimes(1); // never called
});
});
```
์ด ์ ๊ทผ ๋ฐฉ์์ ๋ฌธ์ ๋ ์ค์ ๋ชจ๋์ ์๊ตฌํ ๋ค์ foo
ํธ์ถ์ด ์ฌ์ ํ ์ค์ message
ํจ์๋ฅผ ํธ์ถํ๊ณ ๋ชจ์๋ ํธ์ถ ํ์ง ์๋๋ค๋ ๊ฒ์
๋๋ค.
์ด๋ ๊ฒ ๊ฐ๋จํ์ผ๋ฉด ์ข๊ฒ ์ง๋ง ์ด ์ค๋ ๋์ ์์ ์๋ ๋์์ด ๋์ง ์์ต๋๋ค. ์ฌ๊ธฐ์ ๋๋ฝ๋ ๊ฒ์ด ์์ผ๋ฉด ์๋ ค์ฃผ์ญ์์ค. ๊ธฐ๊บผ์ด ์๋ชป์ ์ธ์ ํ๊ฒ ์ต๋๋ค.
์๋ฃจ์ ์ ์ฐพ๊ณ ์๋ ์ด ๋ฌธ์ ๋ฅผ ์ ํ๋ ์ฌ๋์๊ฒ๋ ํ๋์ ํ์ผ์์ ๋ง์ const/function์ ๋ด๋ณด๋ด๊ณ ํ ์คํธ ์ค์ธ ํ์ผ๋ก ๊ฐ์ ธ์ฌ ๋ ๋ค์์ด ์ ์๊ฒ ๋์์ด ๋๋ ๊ฒ ๊ฐ์ต๋๋ค.
function mockFunctions() { const original = require.requireActual('../myModule'); return { ...original, //Pass down all the exported objects test: jest.fn(() => {console.log('I didnt call the original')}), someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}), } jest.mock('../myModule', () => mockFunctions()); const storage = require.requireMock('../myModule'); `
๋ค์์ ์๋ํ๋ฉฐ ์ฝ๊ฐ ๋ ์งง์ต๋๋ค.
const module = require('./module');
jest.spyOn(module, 'myFn').mockImplementation(() => 'val');
Typescript์์ import
๋์ require
:
import * as module from './module';
์ด๊ฒ์ ์๋ ๊ธฐ๋ฅ์ ์ฝ๊ฒ ๋ณต์ํ๊ณ ๋ชจ์๋ฅผ ๋ช ํํ๊ฒ ํ๋ ์ด์ ์ด ์์ต๋๋ค.
์๋ฃจ์ ์ ์ฐพ๊ณ ์๋ ์ด ๋ฌธ์ ๋ฅผ ์ ํ๋ ์ฌ๋์๊ฒ๋ ํ๋์ ํ์ผ์์ ๋ง์ const/function์ ๋ด๋ณด๋ด๊ณ ํ ์คํธ ์ค์ธ ํ์ผ๋ก ๊ฐ์ ธ์ฌ ๋ ๋ค์์ด ์ ์๊ฒ ๋์์ด ๋๋ ๊ฒ ๊ฐ์ต๋๋ค.
function mockFunctions() { const original = require.requireActual('../myModule'); return { ...original, //Pass down all the exported objects test: jest.fn(() => {console.log('I didnt call the original')}), someFnIWantToCurry: {console.log('I will curry the original') return jest.fn((...args) => original.someFnIWantToCurry(...args)}), } jest.mock('../myModule', () => mockFunctions()); const storage = require.requireMock('../myModule'); `
๋ค์์ ์๋ํ๋ฉฐ ์ฝ๊ฐ ๋ ์งง์ต๋๋ค.
const module = require('./module'); jest.spyOn(module, 'myFn').mockImplementation(() => 'val');
Typescript์์
import
๋์require
:import * as module from './module';
์ด๊ฒ์ ์๋ ๊ธฐ๋ฅ์ ์ฝ๊ฒ ๋ณต์ํ๊ณ ๋ชจ์๋ฅผ ๋ช ํํ๊ฒ ํ๋ ์ด์ ์ด ์์ต๋๋ค.
์, ์, ์ด ๋ฐฉ๋ฒ์ ๊ฐ์ฒด์ getter
์ ์๋์ด ์์ผ๋ฉด ์๋ํ์ง ์์ต๋๋ค. ์ค๋ฅ ๋ฉ์์ง๋ ๋ค์๊ณผ ๊ฐ์ ์ ์์ต๋๋ค.
Test suite failed to run
TypeError: Cannot set property useContent of #<Object> which has only a getter
์ด ๊ฒฝ์ฐ jest.mock(..)
๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. :bowing_man:
๋ด ๋ชจ์๋ ๋ค์์ ์ฌ์ฉํ์ฌ ์๋ํฉ๋๋ค.
import { unsubscribe } from "../lib/my-lib"
import { MyComponent } from "./index"
test("unsubscribe gets called", () => {
const module = require("../lib/my-lib")
jest.spyOn(
module,
"unsubscribe"
).mockImplementation(() => jest.fn())
const { getByTestId } = render(() => <MyComponent />)
let button = getByTestId("trigger.some.action.button")
fireEvent.press(button)
expect(unsubscribe).toHaveBeenCalled()
})
๊ทธ๋ ๊ฒ ์ฐ์ํด ๋ณด์ด์ง๋ ์๊ณ ์ฝ๊ฒ ํ์ฅ๋์ง๋ ์์ต๋๋ค. ๊ทธ๋๋ ์ ์๋ํ๊ณ ์ง๊ธ์ ์ ๊ฒฝ์ฐ์ ์ ํฉํฉ๋๋ค. ํ์ง๋ง ๋๊ตฐ๊ฐ๊ฐ ๋ค๋ฅธ ๊ตฌ๋ฌธ์ ์ ์ํ ์ ์๋ค๋ฉด ํ๋ฅญํ ๊ฒ์ ๋๋ค! ์ด๊ฒ์ ์๋ํ๋ ๊ฒ์ผ๋ก ๋ณด์ด๋ ์ ์ผํ ๊ตฌ๋ฌธ์ ๋๋ค.
es6 ๋ชจ๋ ์ฝ๋:
export const funcA = () => {};
export const funcB = () => {
funcA();
};
CommonJS๋ก ๋ณํํ ํ:
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.funcB = exports.funcA = void 0;
var funcA = function funcA() {};
exports.funcA = funcA; // You can mock or add a spy on this `funcA`
var funcB = function funcB() {
funcA(); // This is still original `funcA`
};
exports.funcB = funcB;
์ด ์ํฉ์ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์๋ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ต๋๋ค.
funcA
์ฌ์ฉํ ์ ์์ต๋๋ค.function funcA() {}
exports.funcA = funcA;
function funcB() {
exports.funcA(); // Now, this `exports.funcA` is added a spy or mocked. Keep the same reference to `funcA`
}
exports.funcB = funcB;
๋๋,
export let funcA = () => {};
export const funcB = () => {
exports.funcA();
};
๋จ์ ํ ์คํธ ๊ฒฐ๊ณผ:
PASS myModule.test.ts (9.225s)
funcB
โ should call funcA (3ms)
-------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
myModule.ts | 100 | 100 | 100 | 100 |
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 10.967s
funcA
๋ฅผ ์กฐ๋กฑํ๊ธฐ ์ํด rewire ํจํค์ง๋ฅผ ์ฌ์ฉํ์ญ์์ค.๊ฒ๋ค๊ฐ require
๊ฐ ์ ํํ ๋ฌด์์ ํ๋์ง ๋ณด๋ ค๋ฉด https://nodejs.org/api/modules.html#modules_exports_shortcut ๋ฌธ์๋ฅผ ์ดํด๋ด์ผ ํฉ๋๋ค.
์ด stackoverflow ๊ฒ์๋ฌผ์ ์๋ฃจ์
์ด ์ ์๊ฒ ํจ๊ณผ์ ์ด์์ต๋๋ค.
https://stackoverflow.com/a/53402206/1217998
๊ธฐ๋ณธ์ ์ผ๋ก ๋จผ์ ๋ณํํ๋ ค๋ ๋ชจ๋ ํจ์๋ฅผ jest.fn
๋ก ๋ณํํฉ๋๋ค.
jest.mock('../../utils', () => {
const actualUtils = jest.requireActual('../../utils');
const originalImplementation = actualUtils.someFun;
return {
...actualUtils,
someFun: jest.fn()
.mockImplementation(originalImplementation),
};
});
const utils = require('../../utils');
๊ทธ๋ฐ ๋ค์ ์ด๋ ๊ฒ ์ํ๊ฑฐ๋ ์กฐ๋กฑํ๋ฉด ํ์์ฒ๋ผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
jest.spyOn(utils, 'someFun').mockReturnValueOnce(true);
์ด๊ฒ์ ์ฌ์ฉํ์ฌ ์๋ ๊ตฌํ์ ์ป์ ์ ์์ต๋๋ค.
beforeEach(() => {
jest.clearAllMocks();
});
์ด stackoverflow ๊ฒ์๋ฌผ์ ์๋ฃจ์ ์ด ์ ์๊ฒ ํจ๊ณผ์ ์ด์์ต๋๋ค.
https://stackoverflow.com/a/53402206/1217998๊ธฐ๋ณธ์ ์ผ๋ก ๋จผ์ ๋ณํํ๋ ค๋ ๋ชจ๋ ํจ์๋ฅผ
jest.fn
๋ก ๋ณํํฉ๋๋ค.jest.mock('../../utils', () => { const actualUtils = jest.requireActual('../../utils'); const originalImplementation = actualUtils.someFun; return { ...actualUtils, someFun: jest.fn() .mockImplementation(originalImplementation), }; }); const utils = require('../../utils');
๊ทธ๋ฐ ๋ค์ ์ด๋ ๊ฒ ์ํ๊ฑฐ๋ ์กฐ๋กฑํ๋ฉด ํ์์ฒ๋ผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
jest.spyOn(utils, 'someFun').mockReturnValueOnce(true);
์ด๊ฒ์ ์ฌ์ฉํ์ฌ ์๋ ๊ตฌํ์ ์ป์ ์ ์์ต๋๋ค.
beforeEach(() => { jest.clearAllMocks(); });
๊ฐ์ฌํฉ๋๋ค!
Jest๋ ๋ฌธ์์ ๋ชจ๋์ ๋ถ๋ถ์ ์ผ๋ก ๋ชจ์ํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๋งค์ฐ ๊ฐ๋จํ๊ณ ๋ช ํํ ์๊ฐ ์์ต๋๋ค. ์ด๊ฒ์ ES import ๋ฐ Node require ๋ฌธ ๋ชจ๋์์ ์๋ํฉ๋๋ค.
https://jestjs.io/docs/en/jest-object#jestrequireactualmodulename
๋ชจ์ ํจ์๊ฐ ๋ชจ๋ ๋ด์์ ํธ์ถ๋๋ฉด ์๋ํ์ง ์์ต๋๋ค.
๋ํ ๋๋ก๋ ์๋ ํจ์๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ์ผ๋ถ ์ฌ์ฉ์ ์ ์(์ถ๊ฐ) ๋ณ์๋ฅผ ์ฌ์ฉํ์ฌ ํจ์๋ฅผ ํธ์ถํ๋ ๋ฐฉ์์ผ๋ก ํจ์๋ฅผ ๋ชจ์ํ๋ ๊ฒ์ด ์ ์ฉํ ์ ์์์ ๋ฐ๊ฒฌํ์ต๋๋ค.
jest.mock('./someModule', () => {
const moduleMock = require.requireActual('./someModule');
return {
...moduleMock,
// will mock this function
someFunction: (args) =>
moduleMock.someFunction({
...args,
customArgument,
}),
};
});
์ ๊ฒฝ์ฐ์๋ ๊ธฐ๋ณธ ๊ตฌ์ฑ์ ์ฌ์ฉํ์ง ์๊ณ ์๋ํ๋๋ก ๊ตฌ์ฑ์ ์ ๋ฌํด์ผ ํ์ต๋๋ค.
์ด๊ฒ์ด ๋ด๊ฐ ์ด๊ฒ์ ํ ์ ์๋ ์ ์ผํ ๋ฐฉ๋ฒ์ด๋ฏ๋ก ๋ ๋ซ๊ฑฐ๋ ๋ ์ฌ์ด ์์ด๋์ด๊ฐ ๋ ์ค๋ฅด๋ฉด ๊ธฐ๊บผ์ด ๋ฃ๊ฒ ๋ ๊ฒ์ ๋๋ค. :)
FWIW https://github.com/magicmark/jest-how-do-i-mock-x/blob/master/src/function-in-same-module/README ์์ ์คํ ๊ฐ๋ฅํ ์์ ์ ํจ๊ป ๋ค์ํ ์ ๊ทผ ๋ฐฉ์์ ๋ชจ์์ต๋๋ค
์ด๊ฒ์ OP ์ง๋ฌธ/๋ฌธ์ ์ ๋๋ตํ์ง ์์ง๋ง ์ผ๋ถ ๋ฆฌํฉํ ๋ง์ด ๊ด๋ จ๋ ์๋ฃจ์ ์ ๋๋ค. ๋ด ๊ธฐ๋ฅ์ ๋ค๋ฅธ ํ์ผ๋ก ๋ถ๋ฆฌํ ๋ค์ ํด๋น ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ์กฐ๋กฑํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ด๋ผ๋ ๊ฒ์ ์์์ต๋๋ค.
// package.json
...
"scripts": {
"test": "jest",
...
"devDependencies": {
"@babel/preset-env": "^7.11.5",
"jest": "^24.9.0",
...
```js
// babel.config.js
module.exports = {
์ฌ์ ์ค์ : [
[
'@babel/preset-env',
{
๋์: {
๋
ธ๋: 'ํ์ฌ',
},
},
],
],
};
```js
// module-utils.js
export const isBabaYaga = () => {
return false
}
// module.js
import { isBabaYaga } from './module-utils'
export const isJohnWickBabaYaga = () => {
return isBabaYaga()
}
// modules.test.js
import { isJohnWickBabaYaga } from './module';
jest.mock('./module-utils', () => ({
isBabaYaga: jest.fn(() => true)
}))
test('John Wick is the Baba Yaga', () => {
// when
const isBabaYaga = isJohnWickBabaYaga()
// then
expect(isBabaYaga).toBeTruthy()
});
PASS src/module.test.js
โ John Wick is the Baba Yaga (4ms)
์ต๊ทผ์ ์ด ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค. ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ ์ ์๊ธฐ ๋๋ฌธ์ ์ ์๋ ์๋ฃจ์ ์ค ์ด๋ ๊ฒ๋ ์๋ํ์ง ์์ต๋๋ค. babel-plugin-rewire๋ ์๋ํ์ง ์์ต๋๋ค. ํจ์๊ฐ ๋์ผํ ๋ชจ๋ ๋ด์ ๋ค๋ฅธ ํจ์์ ์ํด ํธ์ถ๋์๋์ง ํ ์คํธํ๋ ๋ค๋ฅธ ์๋ฃจ์ ์ด ์์ต๋๊น? ์ด๊ฒ์ ์ ์งํ๊ฒ ์๋ํด์ผ ํ๊ฑฐ๋ ์ด ์์ ์ ์ํํ๋ babel ํ๋ฌ๊ทธ์ธ์ด ์์ด์ผ ํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ ๋๋ค. ๋์์ ์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค!
์ต๊ทผ์ ์ด ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค. ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ ์ ์๊ธฐ ๋๋ฌธ์ ์ ์๋ ์๋ฃจ์ ์ค ์ด๋ ๊ฒ๋ ์๋ํ์ง ์์ต๋๋ค. babel-plugin-rewire๋ ์๋ํ์ง ์์ต๋๋ค. ํจ์๊ฐ ๋์ผํ ๋ชจ๋ ๋ด์ ๋ค๋ฅธ ํจ์์ ์ํด ํธ์ถ๋์๋์ง ํ ์คํธํ๋ ๋ค๋ฅธ ์๋ฃจ์ ์ด ์์ต๋๊น? ์ด๊ฒ์ ์ ์งํ๊ฒ ์๋ํด์ผ ํ๊ฑฐ๋ ์ด ์์ ์ ์ํํ๋ babel ํ๋ฌ๊ทธ์ธ์ด ์์ด์ผ ํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ ๋๋ค. ๋์์ ์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค!
https://github.com/facebook/jest/issues/936#issuecomment -659597840์ ํ์ธํ์ จ์ต๋๊น? ๋์ผํ ํ์ผ์์ ํจ์ ํธ์ถ์ ์กฐ๋กฑํ๋ ์ต์ํ์ ์ฌํ์ด ์์ต๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
๋ ํ ์์์ด:
http://facebook.github.io/jest/docs/api.html#mock -functions ์ฐธ์กฐ