때때로 일부 테스트에서는 함수를 모의하고 싶지만 다른 테스트에서는 모의하고 싶지 않을 때가 있습니다. 때로는 서로 다른 테스트에 서로 다른 모의 객체를 제공하고 싶을 때가 있습니다. Jest는 이를 까다롭게 만듭니다. 기본 동작은 단일 테스트가 아닌 전체 테스트 파일에 대한 패키지 기능을 재정의하는 것입니다. Python의 @patch 또는 Laravel의 서비스 컨테이너와 같은 유연한 도구를 사용했다면 이상하게 보일 것입니다.
이 게시물에서는 개별 테스트에 대해 함수를 모의하는 방법을 보여주고, 모의가 제공되지 않은 경우 원래 구현으로 대체합니다. CommonJS 및 ES 모듈 모두에 대한 예제가 제공됩니다. 이 게시물에서 설명하는 기술은 자사 모듈과 타사 패키지 모두에서 작동합니다.
이 게시물에서는 여러 모듈 시스템을 다룰 예정이므로 해당 시스템이 무엇인지 이해하는 것이 중요합니다.
CommonJS(약어로 CJS)는 Node.js의 모듈 시스템입니다. module.exports를 사용하여 함수를 내보내고 require():
를 사용하여 함수를 가져옵니다.
// CommonJS export function greet() { return "Hello, world!"; } module.exports = { greet };
// CommonJS import const getUsersList = require('./greet');
ES 모듈(약칭 ESM)은 브라우저에서 사용되는 모듈 시스템입니다. 내보내기 키워드를 사용하여 함수를 내보내고 가져오기 키워드를 사용하여 함수를 가져옵니다:
// ES module export export default function greet() { return "Hello, world!"; }
// ES module import import { greet } from "./greet";
이 글을 쓰는 시점에는 대부분의 프론트엔드 JavaScript 개발자가 ES 모듈을 사용하고 있으며, 많은 서버 측 JS 개발자도 ES 모듈을 사용하고 있습니다. 그러나 CommonJS는 여전히 Node.js의 기본값입니다. 어떤 시스템을 사용하든 Jest의 조롱 시스템에 대해 알아보려면 전체 기사를 읽어볼 가치가 있습니다.
일반적으로 CommonJS 파일은 아래와 같이 객체 구문을 사용하여 모듈을 내보냅니다.
// CommonJS export function greet() { return "Hello, world!"; } module.exports = { greet: greet };
그러나 함수 자체를 내보낼 수도 있습니다.
// CommonJS export function greet() { return "Hello, world!"; } module.exports = greet;
본인의 코드에서 이 작업을 수행하는 것을 꼭 권장하지는 않습니다. 개체를 내보내면 애플리케이션을 개발하는 동안 골치 아픈 일이 줄어듭니다. 그러나 이는 CommonJS에서 내보낸 함수를 모의하는 방법을 논의한 다음 테스트에서 자체 구현을 제공하지 않는 경우 원본으로 대체하는 방법을 논의할 가치가 있을 만큼 충분히 일반적입니다.
테스트 중에 모의하려는 다음 CommonJS 파일이 있다고 가정해 보겠습니다.
// cjsFunction.js function testFunc() { return "original"; } module.exports = testFunc;
다음 코드를 사용하여 테스트에서 이를 모의할 수 있습니다.
const testFunc = require("./cjsFunction"); jest.mock("./cjsFunction"); beforeEach(() => { testFunc.mockImplementation(jest.requireActual("./cjsFunction")); }); it("can override the implementation for a single test", () => { testFunc.mockImplementation(() => "mock implementation"); expect(testFunc()).toBe("mock implementation"); expect(testFunc.mock.calls).toHaveLength(1); }); it("can override the return value for a single test", () => { testFunc.mockReturnValue("mock return value"); expect(testFunc()).toBe("mock return value"); expect(testFunc.mock.calls).toHaveLength(1); }); it("returns the original implementation when no overrides exist", () => { expect(testFunc()).toBe("original"); expect(testFunc.mock.calls).toHaveLength(1); });
jest.mock("./cjsFunction")을 호출하면 모듈(파일 및 모든 내보내기)이 자동 모의(문서)로 대체됩니다. 자동 모의가 호출되면 정의되지 않은 상태가 반환됩니다. 그러나 모의 구현, 반환 값 등을 재정의하는 메서드를 제공합니다. Jest Mock Functions 문서에서 제공되는 모든 속성과 메서드를 볼 수 있습니다.
모의의 mockImplementation() 메서드를 사용하여 모의의 구현을 원래 모듈의 구현으로 자동 설정할 수 있습니다. Jest는 현재 조롱되고 있는 경우에도 원본 모듈을 항상 로드하는 jest.requireActual() 메서드를 제공합니다.
모의 구현과 반환 값은 각 테스트 후에 자동으로 지워지므로 각 테스트 전에 모의 구현을 원래 구현으로 설정하는 Jest의 beforeEach() 함수에 콜백 함수를 전달할 수 있습니다. 그런 다음 자체 반환 값이나 구현을 제공하려는 테스트는 테스트 본문 내에서 수동으로 수행할 수 있습니다.
위의 코드가 단일 함수 대신 객체를 내보냈다고 가정해 보겠습니다.
// cjsModule.js function testFunc() { return "original"; } module.exports = { testFunc: testFunc, };
테스트는 다음과 같습니다.
const cjsModule = require("./cjsModule"); afterEach(() => { jest.restoreAllMocks(); }); it("can override the implementation for a single test", () => { jest .spyOn(cjsModule, "testFunc") .mockImplementation(() => "mock implementation"); expect(cjsModule.testFunc()).toBe("mock implementation"); expect(cjsModule.testFunc.mock.calls).toHaveLength(1); }); it("can override the return value for a single test", () => { jest.spyOn(cjsModule, "testFunc").mockReturnValue("mock return value"); expect(cjsModule.testFunc()).toBe("mock return value"); expect(cjsModule.testFunc.mock.calls).toHaveLength(1); }); it("returns the original implementation when no overrides exist", () => { expect(cjsModule.testFunc()).toBe("original"); }); it("can spy on calls while keeping the original implementation", () => { jest.spyOn(cjsModule, "testFunc"); expect(cjsModule.testFunc()).toBe("original"); expect(cjsModule.testFunc.mock.calls).toHaveLength(1); });
jest.spyOn() 메소드를 사용하면 Jest가 객체의 메소드에 대한 호출을 기록하고 자체 대체를 제공할 수 있습니다. 이 만 개체에 대해 작동하며 모듈이 함수가 포함된 개체를 내보내고 있기 때문에 이를 사용할 수 있습니다.
spyOn() 메서드는 모의 메서드이므로 해당 상태를 재설정해야 합니다. Jest spyOn() 문서에서는 위에서 수행한 afterEach() 콜백에서 jest.restoreAllMocks()를 사용하여 상태를 재설정할 것을 권장합니다. 이 작업을 수행하지 않으면 spyOn()이 호출된 후 다음 테스트에서 모의 개체가 정의되지 않은 상태를 반환하게 됩니다.
ES 모듈은 기본 및 명명된 내보내기를 가질 수 있습니다:
// esmModule.js export default function () { return "original default"; } export function named() { return "original named"; }
위 파일에 대한 테스트는 다음과 같습니다.
import * as esmModule from "./esmModule"; afterEach(() => { jest.restoreAllMocks(); }); it("can override the implementation for a single test", () => { jest .spyOn(esmModule, "default") .mockImplementation(() => "mock implementation default"); jest .spyOn(esmModule, "named") .mockImplementation(() => "mock implementation named"); expect(esmModule.default()).toBe("mock implementation default"); expect(esmModule.named()).toBe("mock implementation named"); expect(esmModule.default.mock.calls).toHaveLength(1); expect(esmModule.named.mock.calls).toHaveLength(1); }); it("can override the return value for a single test", () => { jest.spyOn(esmModule, "default").mockReturnValue("mock return value default"); jest.spyOn(esmModule, "named").mockReturnValue("mock return value named"); expect(esmModule.default()).toBe("mock return value default"); expect(esmModule.named()).toBe("mock return value named"); expect(esmModule.default.mock.calls).toHaveLength(1); expect(esmModule.named.mock.calls).toHaveLength(1); }); it("returns the original implementation when no overrides exist", () => { expect(esmModule.default()).toBe("original default"); expect(esmModule.named()).toBe("original named"); });
이는 몇 가지 주요 차이점을 제외하면 이전 CommonJS 예제와 거의 동일해 보입니다.
먼저 모듈을 네임스페이스 가져오기로 가져옵니다.
import * as esmModule from "./esmModule";
그런 다음 기본 내보내기를 감시하려면 "default"를 사용합니다:
jest .spyOn(esmModule, "default") .mockImplementation(() => "mock implementation default");
때때로 타사 패키지로 jest.spyOn()을 호출하려고 하면 아래와 같은 오류가 발생합니다.
TypeError: Cannot redefine property: useNavigate at Function.defineProperty ()
이 오류가 발생하면 문제를 일으키는 패키지를 모의해야 합니다.
import * as reactRouterDOM from "react-router-dom"; // ADD THIS: jest.mock("react-router-dom", () => { const originalModule = jest.requireActual("react-router-dom"); return { __esModule: true, ...originalModule, }; }); afterEach(() => { jest.restoreAllMocks(); });
이 코드는 모듈을 jest.mocks의 팩토리 매개변수를 사용하여 모듈의 원래 속성을 모두 포함하는 Jest ES 모듈 모의로 대체합니다. __esModule 속성은 ES 모듈을 모의하기 위해 jest.mock의 팩토리 매개변수를 사용할 때마다 필요합니다(문서).
원한다면 공장 매개변수의 개별 기능을 대체할 수도 있습니다. 예를 들어 소비자가 Router 컨텍스트 외부에서 useNavigate()를 호출하면 React Router는 오류를 발생시키므로 원하는 경우 jest.mock()을 사용하여 전체 테스트 파일에서 해당 함수를 대체할 수 있습니다.
jest.mock("react-router-dom", () => { const originalModule = jest.requireActual("react-router-dom"); return { __esModule: true, ...originalModule, // Dummy that does nothing. useNavigate() { return function navigate(_location) { return; }; }, }; });
이 정보가 자신의 테스트를 작성할 때 유용하길 바랍니다. 테스트 자체에서 구현이 제공되지 않는 경우 모든 앱이 기본 구현으로 대체할 수 있는 이점을 얻을 수 있는 것은 아닙니다. 실제로 많은 앱은 전체 테스트 파일에 대해 동일한 모의 파일을 사용하기를 원할 것입니다. 그러나 이 게시물에 표시된 기술을 사용하면 조롱을 세밀하게 제어할 수 있습니다.
제가 놓친 부분이 있거나 여기에 있어야 할 이 게시물에 포함하지 않은 내용이 있으면 알려주세요.
부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.
Copyright© 2022 湘ICP备2022001581号-3