November 21, 2022
이전 글 > 테스트에 대한 오해와 사실
Table of contents
테스트 케이스간 공유하는 객체를 만들지 않는다.
전후 맥락이 필요한 테스트 케이스를 만들지 않는다.
제품이 돌아가는 환경과 테스트 코드가 돌아가는 환경이 다르다.
테스트 대역이 많이 필요하다.
테스트 케이스를 수행하는데 외부 의존성을 대체하기 위한 수단을 일컫는 말
제품이 돌아가는 환경과 테스트 코드가 돌아가는 환경이 다르다. (node vs browser)
→ browser라는 ‘의존성’, 기능을 제공하는 외부 의존성이 있어서 어려웠던 것이다.
→ 브라우저라는 런타임을 주입받아야 하는가?
JavaScript 언어는 몽키패칭하기 쉽다. (
jest.mock
)
사전 그대로 받아들이면 이해하기 어려운 용어
의존
+성
의존하다.
A가 B에 의존한다.
=== 기능을 사용한다.
=== 메세지를 보낸다.
‘왜 그것에 의존하는가?’로 접근을 해야 ‘의존성’을 이해할 수 있다.
의존성
⇒ 서비스? 런타임?
Browser Runtime API (Web API) → window.*
DOM (Document Object Model) → document.*
데이터를 주고 받는 외부의 무언가
주입의 정의
주입한다는 것은 객체의 생성과 사용의 관심을 분리하는 것을 말한다.
이게 테스트랑 무슨 상관?
주입받는다는 것은?
A에서 b에 직접적으로 의존할 필요가 없다.
사실 A입장에서 b인지 아닌지는 중요하지 않고 b에게 요청할 기능, 메세지가 중요했다.
→ 우리는 이것을 인터페이스(또는 계약)라고 부를 수 있다. ⇒ M
1) 직접적으로 의존하는 경우
import LogClient from '@logging-sdk/core';
import { useEffect } from 'react';
function ServicePage() {
useEffect(() => {
LogClient.log(...)
}, [])
return <div>...</div>
}
ServicePage
라는 컴포넌트는 LogClient
에 직접적으로 의존한다.만약 테스트를 한다면?
LogClient.log
의 구현이 외부에 의존하고 있을 경우 테스트하기 어렵다.2) 의존성을 주입받는 경우
DI 구현을 위해 Context API와 hooks를 사용해보자.
ServicePage.tsx
import { useLogClient } from '@logging-sdk/react';
import { useEffect } from 'react';
function ServicePage() {
const client = useLogClient();
useEffect(() => {
client.log(...)
}, [])
return <div>...</div>
}
App.tsx
function App() {
return (
<LogProvider client={new LogClient()}>
<ServicePage />
</LogProvider>
);
}
LogClient
를 생성하고 주입!ServicePage.test.tsx
test('ServicePage 가 렌더링되면 Screen 로깅을 한다.', () => {
const logClient = new DebugLogClient();
render(<ServicePage />, {
wrapper: ({children}) => {
return <LogProvider client={logClient}>{children}</LogProvider>
}
})
});
LogProvider.tsx
import LogClient from '@logging-sdk/core'; // ??
interface Props {
client: LogClient; // FIXME
children: ReactNode;
}
export function LogProvider({ client, children }: Props) {
return <LogContext.Provider value={{ client }}>{children}</LogContext.Provider>;
}
LogClient.tsx
interface LogClientSpec {
log: (...) => void;
}
export class LogClient implements LogClientSpec {}
export class DebugLogClient implements LogClientSpec {}
import { LogClientSpec } from '@logging-sdk/core'; // ??
interface Props {
client: LogClientSpec;
children: ReactNode;
}
export function LogProvider({ client, children }: Props) {
return <LogContext.Provider value={{ client }}>{children}</LogContext.Provider>;
}