front-end Test
도입부
행복한 가정은 서로 닮았지만 불행한 가정은 모두 저마다의 이유로 불행하다 [톨스토이 - 안나 카레니나, 2011 번역 - 펭귄클래식코리아]
하나의 문제는 하나의 원인으로 이루어 지지 않는다
테스트는 deterministic 해야한다. (언제 실행되든 항상 같은 결과를 내야한다.)
issue
[Error: This browser does not support 'ResizeObserver' out of the box. Please provide a polyfill as a prop.]
- 해결
import 'resize-observer-polyfill/dist/ResizeObserver.global'
관련 글들
정리 - 노트 클립 - 구글은 어떻게 소프트웨어를 테스트하는가
toast UI: 테스트
- 단위 테스트
- 주로 모듈 단위
- 통합 테스트
- 두 개 이상의 모듈이 연결된 상태를 테스트
-
E2E 테스트
- 실제 사용자의 관점에서 테스트를 진행하며, 그런 의미에서 기능테스트 혹은 UI 테스트라고 불리기도 한다
- 테스트 코드가 실제 코드 내부 구조에 영향을 받지 않기 때문에 큰 범위의 리팩토링에도 깨지지 않으며, 이를 통해 개발자들이 좀 더 자신감 있게 코드를 개선할 수 있도록 도와준다
- 테스트 작성하기 어렵다
- 테스트 사이의 중복이 발생한다
- 예상 밖의 문제들(네트워크 오류, 프로세스 대기로 인한 타임아웃)으로 인해 테스트가 가끔 실패하는 일이 발생
- 멀티 브라우저 테스트 환경 구축하기
-
-
WebDriver는 기본적으로 브라우저가 서버 역할을 하고 제어를 요청하는 기기(개발자 PC 혹은 CI 서버)가 클라이언트의 역할을 하는 서버-클라이언트 구조라고 할 수 있으며, 브라우저용 드라이버와 개발자용 클라이언트를 설치해서 사용하게 된다.
-
E2E 테스트의 단점을 그대로 갖고 있기 때문에 테스트를 작성하거나 유지 보수하는데 많은 비용이 들며, 이로 인해 개발자들이 개발 단계에서 사용하는 테스트 도구로써는 사실상 널리 활용되지 못하고 있다.
-
프레임워크
- Protractor : Angular 프로젝트를 위한 테스트 프레임워크
- WebdriverJS : Selenium Webdriver의 정식 Node.js 구현체이며, 낮은 수준(Low-level)의 API를 제공한다.
- NightWatch : Mocha 기반의 테스트 러너와 직관적인 API, CI 서버 통합 등의 다양한 기능을 지원한다.
- WebdriverIO : 테스트 러너, 정적 웹 서버, CI 서버 통합, REPL 인터페이스 등 다양한 기능을 지원하며 커뮤니티가 가장 잘 활성화되어 있다.
-
- Cypress
- Cypress는 Selenium WebDriver와는 전혀 다른 목적을 갖는 도구이며, 정확히 프론트엔드 개발자들이 개발 단계에서 사용하기에 최적화된 도구라고 할 수 있다
- WebDriver와는 다르게 실제 애플리케이션과 테스트 코드를 동일한 브라우저에서 실행하는 방식을 취하고 있다
- 브라우저 기반의 GUI를 사용하여 테스트의 실행 상태를 확인하고 디버깅할 수 있는 다양한 편의 기능을 제공한다.
- 실행된 모든 테스트 명령과 각 명령이 실행될 때의 UI 상태를 스냅샷 형태로 모두 저장하고 있어, 특정 시점의 UI 상태를 눈으로 확인할 수 있다
- 또한 전체 테스트 진행 과정을 동영상으로 저장하거나 테스트가 실패했을 때 자동으로 스크린샷을 남길 수 있어 테스트가 실패했을 때 원인을 파악하기가 매우 쉽다
- 게다가 브라우저에서 실행되기 때문에 필요한 경우 크롬 개발자 도구를 사용해 디버깅을 할 수도 있다.
- 브라우저 내부에서 실행되는 방식에는 단점
- 브라우저의 새 탭 혹은 새 창을 열 수 없으며
- 동일 출처(Same-origin) 정책을 벗어나는 페이지로는 이동을 할 수가 없다
용어 정리
- TDD: test driven development
- BDD: Behaviour-Driven Development
- 어떤 행위를 해야한다. (should do someting)
- BDD는 비즈니스 요구사항에 집중하여 테스트 케이스
경우에 관한 테스트 생각 by toyjhlee
- lib
- lib 를 믿어라. input 이 올바른지 확인!
Test Runner Test Mathcher Test Mock
Libaray
reat-testing-library
api-queries
cheatsheet
testing-library / jest-dom - Custom matchers
examples
React Unit Test — react-testing-library, 너를 알아가는 시간
- Enzyme 에서 react-testing-library 변경 관련
ReferenceError: document is not defined
-
message
The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/en/configuration#testenvironment-string. Consider using the "jsdom" test environment. ReferenceError: document is not defined
-
해결방법
- testEnvironment 를 node -> jsdom 으로 변경 in package.json
- jest 재 실행
Property 'toBeInTheDocument' does not exist on type 'Matchers<any>
- message
Property 'toBeInTheDocument' does not exist on type 'JestMatchersShape<HTMLElement[], Matchers<void, HTMLElement[]>, Matchers<Promise<void>, HTMLElement[]>>'.
-
해결방법
-
[Property ‘toBeInTheDocument’ does not exist on type ‘Matchers
'](https://stackoverflow.com/questions/57861187/property-tobeinthedocument-does-not-exist-on-type-matchersany) -
import '@testing-library/jest-dom/extend-expect
-
jest
daleseo.com
- Jest로 기본적인 테스트 작성하기
- Jest로 테스트 전/후 처리하기
- Jest로 비동기 코드 테스트 작성하기
- [Jest] jest.fn(), jest.spyOn() 함수 모킹
- [Jest] jest.mock() 모듈 모킹
snapshot-testing
- 왜 스냅 샷 테스트인가?
- 스냅 샷 통합 테스트가 해결하는 엔드 투 엔드 테스트에서 많은 문제를 발견했습니다.
- 스냅 샷 테스트가 시각적 회귀 테스트 대비 장점
- 결함 없음
- 빠른 반복 속도
- 디버깅
- 스냅 샷 테스트가 시각적 회귀 테스트 대비 장점
- 스냅 샷 통합 테스트가 해결하는 엔드 투 엔드 테스트에서 많은 문제를 발견했습니다.
- -u 플래그를 사용하여 스냅 샷을 다시 생성 할 수도 있습니다
- toMatchInlineSnapshot
- 스냅 샷 값이 소스 코드에 자동으로 다시 기록된다는 점을 제외하면 외부 스냅 샷 ( 파일) 과 동일하게 작동합니다 . 즉, 올바른 값이 기록되었는지 확인하기 위해 외부 파일로 전환하지 않고도 자동으로 생성 된 스냅 샷의 이점을 얻을 수 있습니다.
Testing Sass with Jest
- Testing that your Sass compiles without errors
- Enforcing good Sass hygiene
- Testing Sass functions
- Testing Sass mixins
sass file 의 내용을 테스트 한다
// root.module.scss
:root {
}
const util = require('util')
const sass = require('node-sass')
const sassRenderPromisify = util.promisify(sass.render)
export const sassRender = (options: any) => {
return sassRenderPromisify({
// Where node-sass should look for files when you use @import
includePaths: ['./sass'],
// Using a compact output style allows you to use concise 'expected' CSS
outputStyle: 'compact',
// Merge in any other options you pass when calling render
...options,
})
}
it('', async () => {
const rootModule = await sassRender({
file: 'src/root.module.scss',
})
expect(rootModule.css.toString()).toContain(':root {')
})
Jest — How to Use Extend with TypeScript
declare global {
namespace jest {
interface Matchers<R> {
withOut(expected: any): R
}
}
}
expect.extend({
withOut(actual: any, expected: any) {
const pass = actual % expected === 0
const message = pass
? () => `expected ${actual} not to be without by ${expected}`
: () => `expected ${actual} to be without by ${expected}`
return {message, pass}
}
})
canvas mock
// setupTests.ts
import 'jest-canvas-mock'
getContext mock
function mockCanvas(window) {
window.HTMLCanvasElement.prototype.getContext = function () {
return {
fillRect: function () {},
clearRect: function () {},
getImageData: function (x, y, w, h) {
return {
data: new Array(w * h * 4),
}
},
putImageData: function () {},
createImageData: function () {
return []
},
setTransform: function () {},
drawImage: function () {},
save: function () {},
fillText: function () {},
restore: function () {},
beginPath: function () {},
moveTo: function () {},
lineTo: function () {},
closePath: function () {},
stroke: function () {},
translate: function () {},
scale: function () {},
rotate: function () {},
arc: function () {},
fill: function () {},
measureText: function () {
return {width: 0}
},
transform: function () {},
rect: function () {},
clip: function () {},
}
}
window.HTMLCanvasElement.prototype.toDataURL = function () {
return ''
}
}
const document = jsdom.jsdom(undefined, {
virtualConsole: jsdom.createVirtualConsole().sendTo(console),
})
const window = document.defaultView
mockCanvas(window)
worker mock
대상 코드
import ExternalSourceLoaderWorker from "comlink-loader!./ExternalSourceLoaderWorker"; /
mock 코드
jest.mock('../worker', () => {
return jest.fn().mockImplementation(() => ({
ExternalSourceLoaderWorker: jest.fn(),
}))
})
private method mock
class Foo {
private prop: string;
}
const foo = new Foo()
jest.spyOn<any, string>(foo, 'prop');
Testing Sass with Jest
toHaveStyleRule 에 ruls 이 많은 경우
const toHaveStyleRules = (component, property, options) => {
let hyphen = ''
_.each(property, (value, key) => {
hyphen = key.replace(/([A-Z])/g, g => `-${g[0].toLowerCase()}`)
expect(component).toHaveStyleRule(hyphen, value, options)
})
}
window 에 값 설정
jest 실행 시 내부의 class 가 window 의 지정한 property 를 참조하는 경우 아래 방법으로는 undefind 가 발행한다
Object.defineProperty(window, key, {
value: {},
})
Object.defineProperties(window, {
key: {
value: {},
},
})
아래 방법으로 해야 한다
// setupTests
window.name = {}
Timer Mocks
jest.advanceTimersByTime(1000);
7 Ways to Debug Jest Tests in Terminal
- The Standard Way
- Without an Initial Break
- Debugging TypeScript Tests
- Best Way To Debug ™️
- What About CRA?
- Debugging From the Command-Line
- All-In-One solution - ndb
visual regression testing(시각적 회귀 테스트)
E2E
- E2E 테스트
- E2E 테스트와 나이트왓치
- Selenium에서 Cypress로 갈아탄 후기
- 결론은 Cypress가 프런트엔드 개발자를 대상으로 한다는것과 Selenium은 QA개발자를 대상으로 한다는 것이 핵심이었다고 할 수 있다.
robinwieruch
- react-testing-library
- How to test React-Redux connected Components
- How to test React with Jest & Enzyme
form 관련 예시 및 설명
Difference between enzyme, ReactTestUtils and react-testing-library
Redux
-
Redux useSelector and useDispatch hook test example
- redux-mock-store 사용
const configureStore = require('redux-mock-store').default
- redux-mock-store 사용
- Writing Tests - Redux 공식홈
- React + Redux 앱 테스트
- Testing Redux - reactjsday
분류 전
링크들
-
입에 쓴 보약, TDD와 BDD - 2013.05 마소 VOL. 355-2013.05
- 화성에서 온 TDD, 금성에서 온 BDD?
-
번역 - 리액트 + 리덕스 앱을 Jest와 Enzyme으로 테스트 하며 얻은 교훈
- 같은 환경이 아니어도 읽어볼 가치가 있다
-
Enzyme’s TDD approach and react-testing-library’s BDD
-
삭제 가능 할 듯 Enzyme vs. react-testing-library: A mindset shift
- React-testing-library
- 장점 : jest-dom 을 사용하여 좀 더 쉽게 만들 수 있음
- 단점 : state 에 접근 할 수 없음
- logrocket 소개
- React-testing-library
-
Static vs Unit vs Integration vs E2E Testing for Frontend Apps