# Vue Test Utils
λ·° ν μ€νΈ μ νΈ(Vue Test Utils (opens new window))μ μ½μ΄ ν λ©€λ²κ° μ μν ν μ€ν 보쑰 λΌμ΄λΈλ¬λ¦¬μ λλ€. μ μ€νΈ(Jest) λΏλ§ μλλΌ λ€λ₯Έ ν μ€νΈ λꡬλ μ¬μ©ν μ μμ΅λλ€.
# μ΅μ Vue-CLI (3.x λ²μ μ΄μ)μμ μ€μΉ λ°©λ²
μλ‘μ΄ νλ‘μ νΈμ κ²½μ° κΈ°λ³Έ νκ²½μ΄ μ€μ λ κΈ°λ³Έ ν리μ
(Default)
μ μ¬μ©ν μ μμ§λ§, λ·° ν
μ€νΈ μ νΈμ μ€μΉνκΈ° μν΄ μλμΒ κ°μ΄ ManuallyΒ selectΒ features μ΅μ
μΒ μ νν΄μ€λλ€.
λ·° ν μ€νΈ μ νΈμ μ€μΉνκΈ° μν΄ Unit Testing μ΅μ μ μ νν΄μ€λλ€.
TIP
λ°©ν₯ν€(β, β)λ‘ νλͺ©μ μ΄λν μ μκ³ space
ν€λ‘ μ ν/ν΄μ , enter
ν€λ‘ κ²°μ ν μ μμ΅λλ€.
# Vue CLI μ΅μ μ ν
Unit Testing μ΅μ μ μΆκ°νκ³ λλ©΄ μλμ κ°μ΄ μ°¨λ‘λλ‘ μ νν©λλ€.
- λ¨Όμ μ½λ μ 리 λκ΅¬μΈ Prettierμ λ¬Έλ² κ²μ¬ λκ΅¬μΈ ESLintλ₯Ό μ νν©λλ€.
- λ€μμ λ¬Έλ² κ²μ¬ λꡬμ μ€ν μμ μ μ νν©λλ€. μλν°μμ μ μ₯μ λλ₯Ό λλ§λ€ κ²μ¬νλ κ²μΌλ‘ μ νν©λλ€.
- λ¨μ ν μ€νΈ λꡬλ μ μ€νΈλ‘ μ νν©λλ€.
- μμμ μΆκ°ν ESLintμ ν리ν°μ΄μ μ€μ λ΄μ©μ package.jsonμ μΆκ°νμ§ μκ³ κ°λ³ μ€μ νμΌμ κ΄λ¦¬ν©λλ€.
# κΈ°μ‘΄ Vue-CLI (2.x λ²μ μ΄ν)μμ μ€μΉ λ°©λ²
μλμ λͺ λ Ήμ΄λ‘ λ·° ν μ€νΈ μ νΈ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ€μΉν©λλ€.
npm install jest @vue/test-utils vue-jest babel-jest --save-dev
μ λͺ
λ Ήμ΄λ‘ vue test util
, jest
, vue-jest
, babel-jest
4κ°μ λΌμ΄λΈλ¬λ¦¬κ° μ€μΉλ©λλ€.
# babel μ¬μ©νκΈ°
νλ‘μ νΈμ λ°λ²¨(babel) (opens new window)μ μ€μ ν μ μ΄ μλ€λ©΄ μλμ κ°μ΄ μ€μΉν΄μ€λλ€.
npm install @babel/core @babel/preset-env babel-core@^7.0.0-bridge.0 --save-dev
λ°λ²¨μ μλ°μ€ν¬λ¦½νΈ(JavaScript) μ»΄νμΌλ¬λ‘μ μμ±ν μ΅μ μ½λ(ECMAScript 2015 λ²μ μ΄μ)λ₯Ό μ΄μ λ²μ (μ€λλ λΈλΌμ°μ λλ νκ²½)μ νΈννμ¬ λμν μ μλλ‘ μ½λλ₯Ό λ³νν΄μ£Όλ λꡬμ λλ€. μλ₯Ό λ€μ΄ μλ μ½λμ κ°μ΄ ES2015+ λ‘ μμ±λ λ¬Έλ²μ μ΄μ μλ°μ€ν¬λ¦½νΈ λ¬Έλ²μΌλ‘ λ³νν΄ μ€λλ€.
// λ°λ²¨ μ
λ ₯: ES2015 νμ΄ν ν¨μ
[1, 2, 3].map(n => n + 1);
// λ°λ²¨ μΆλ ₯: λ³νλ μ½λ
[1, 2, 3].map(function(n) {
return n + 1;
});
λ°λ²¨ ν리μ
μ package.json
λλ babel.config.json
λλ babel.config.js
μμ μ€μ ν μ μμ΅λλ€.
// package.json
{
// ...
"babel": {
"presets": ["@babel/preset-env"]
}
}
// babel.config.json
{
"presets": ["@babel/preset-env"]
}
// babel.config.js
module.exports = {
presets: ["@babel/preset-env"] // μλ μ€μ
presets: ["@vue/cli-plugin-babel/preset"] // vue cliλ‘ μ€μΉν κ²½μ° μλ μ€μ λ¨
};
μ μ€νΈμ κ΄λ ¨λ νκ²½ μ€μ μ μ μ€νΈ νκ²½ μ€μ μμ νμΈν΄λ³΄μΈμ.
- transformμ babel-jest
# μΉν©(Webpack) λ³μΉ(Alias) μ¬μ©
// package.json
{
"jest": {
"moduleNameMapper": {
// λ³μΉ @(νλ‘μ νΈ/src) μ¬μ©νμ¬ νμ κ²½λ‘μ νμΌμ 맡νν©λλ€
"^@/(.*)$": "<rootDir>/src/$1"
}
}
}
// jest.config.js
module.exports = {
moduleNameMapper: {
// λ³μΉ @(νλ‘μ νΈ/src) μ¬μ©νμ¬ νμ κ²½λ‘μ νμΌμ 맡νν©λλ€
'^@/(.*)$': '<rootDir>/src/$1'
},
};
νλ‘μ νΈ κ²½λ‘/src
κΉμ§ κ²½λ‘λ₯Ό @
λ³μΉμΌλ‘ 맡νν©λλ€.
// 'νλ‘μ νΈ κ²½λ‘/src/' νμμ μ‘΄μ¬νλ νμΌμ μλμ κ°μ΄ κ°μννμ¬ μμ±ν μ μμ΅λλ€
import HelloWorld from "@/components/HelloWorld.vue";
TIP
νλ‘μ νΈμ μΉν©μ μ¬μ© μ€μ΄κ³ λ³μΉμ μ¬μ©νλ€λ©΄ μ μ€νΈ νκ²½ μ€μ μλ λμΌνκ² μΆκ°ν΄μ€μΌ ν©λλ€. μΉν©κ³Ό μκ΄μμ΄ ν μ€νΈ μ½λλ₯Ό μν΄ λ³μΉμ μ¬μ©νλ λ°©μλ μΌκ΄μ± μλ κ²½λ‘λ₯Ό μ¬μ©ν μ μμ΄μ μ’μ΅λλ€.
# μ½λ 컀λ²λ¦¬μ§(Code Coverage)
μ μ€νΈλ ν μ€νΈμ μ±κ³΅, μ€ν¨ κ°μλ₯Ό λνλ΄λ κ²°κ³ΌλΏλ§ μλλΌ ν μ€νΈ 컀λ²λ¦¬μ§λ₯Ό λνλ΄λ μ§ν λ³΄κ³ μλ μμ±ν μ μμ΅λλ€.
μλμ κ°μ΄ μ μ€νΈ νκ²½ μ€μ μ μ μ©ν ν
// packages.json
{
"jest": {
"collectCoverage": true,
"collectCoverageFrom": [
"**/*.{js,vue}",
"!**/node_modules/**"
]
}
}
// jest.config.js
module.exports = {
collectCoverage: true,
collectCoverageFrom: [
'**/*.{js,vue}',
'!**/node_modules/**'
]
};
ν μ€νΈλ₯Ό μ€νν΄λ³΄λ©΄ ν°λ―Έλμ μλμ κ°μ΄ ν νμμΌλ‘ κ²°κ³Όλ₯Ό 보μ¬μ€λλ€.
PASS tests/unit/example.spec.js
HelloWorld.vue
β renders props.msg when passed (14ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 0 | 0 | 0 | 0 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.628s
Ran all test suites.
Done in 2.46s.
κ° μ»€λ²λ¦¬μ§ νλͺ©μ μ€λͺ
- Stmts: μ΅μ ν λ² μ΄μ μ€νλ λͺ λ Ήλ¬Έ(λ³μμ κ° μ μ₯, ν¨μ νΈμΆ λ±) μ½λμ λΉμ¨
- Branch: μ΅μ ν λ² μ΄μ if, switchμ κ°μ λΆκΈ° μ‘°κ±΄μ΄ μΆ©μ‘±λ λΉμ¨
- Funcs: μ΅μ ν λ² μ΄μ νΈμΆλ ν¨μμ λΉμ¨
- Lines: μ΅μ ν λ² μ΄μ μ€νλ μ½λ λΌμΈμ λΉμ¨
- Uncovered Line: μ½λ 컀λ²λ¦¬μ§μ μΈ‘μ λμ§ μμ μ½λ λΌμΈ μ
# μ μ€νΈ νκ²½ μ€μ
package.json
μμ μ€μ νκ±°λ jest.config.js
μμ μ€μ ν μ μμ΅λλ€.
# package.json μ€μ
μ€μΉνκ³ λμ package.json
νμΌμ μλμ μ΅μ
μ μΆκ°ν©λλ€.
{
// ...
"jest": {
// vue-cli ν
μ€νΈ νκ²½ μ€μ μ μ¬μ©ν©λλ€
// μ£Όμ! preset μ§μ ν μλμ κ°μ΄ κ°κ° λ€μ μ€μ νλ κ²½μ°, μλ‘ μ€μ ν λ΄μ©μΌλ‘ μ μ©λ©λλ€
"preset": "@vue/cli-plugin-unit-jest",
"moduleFileExtensions": [
"js",
"json",
// λͺ¨λ vue νμΌ(`*.vue`)μ μ²λ¦¬νκΈ° μν΄ Jestμκ² μλ €μ€λλ€
"vue"
],
"transform": {
// `vue-jest`λ₯Ό μ¬μ©νμ¬ λͺ¨λ vue νμΌ(`*.vue`)μ μ²λ¦¬ν©λλ€
".*\\.(vue)$": "vue-jest",
// `babel-jest`λ₯Ό μ¬μ©νμ¬ λͺ¨λ js νμΌ(`*.js`)μ μ²λ¦¬ν©λλ€
".*\\.(js)$": "babel-jest",
},
"moduleNameMapper": {
// λ³μΉ @(νλ‘μ νΈ/src) μ¬μ©νμ¬ νμ κ²½λ‘μ νμΌμ 맡νν©λλ€
"^@/(.*)$": "<rootDir>/src/$1"
},
"testMatch": [
// __tests__ κ²½λ‘ νμμ μλ λͺ¨λ js/ts/jsx/tsx νμΌμ ν
μ€νΈ λμμΌλ‘ μ§μ ν©λλ€
"**/__tests__/**/*.[jt]s?(x)",
// νμΌ μ΄λ¦μ 'xxx.spec' λλ 'xxx.test'λΌλ μ΄λ¦μ΄ λΆμ¬μΈ λͺ¨λ js/ts/jsx/tsx νμΌμ ν
μ€νΈ λμμΌλ‘ μ§μ ν©λλ€
"**/?(*.)+(spec|test).[jt]s?(x)"
],
// node_modules κ²½λ‘ νμμ μλ λͺ¨λ ν
μ€νΈ νμΌμ λμμμ μ μΈν©λλ€
"testPathIgnorePatterns": ["/node_modules/"],
"collectCoverage": true,
"collectCoverageFrom": [
"**/*.{js,vue}",
"!**/node_modules/**"
]
}
}
DANGER
JSON νμΌμ΄λ―λ‘ λ³΅μ¬ν΄ λΆμ¬λ£μ λ μ£Όμμ μ κ±°ν΄μ£ΌμΈμ.
# jest.config.js μ€μ
Vue-CLIλ₯Ό μ΄μ©νμ¬ Unit Testing
μ μ ννλ€λ©΄ jest.config.js
νμΌμ μλμΌλ‘ μμ±ν©λλ€. npmμΌλ‘ μ§μ μ€μΉνκ±°λ, μμ§ νμΌμ΄ μ‘΄μ¬νμ§ μλλ€λ©΄ νλ‘μ νΈ κ²½λ‘(μ΅μμ)μ jest.config.js
νμΌμ μμ±ν΄μ€λλ€.
module.exports = {
// (vue-cliλ‘ μ€μΉ μ κΈ°λ³Έ μΈν
λ¨) vue-cli ν
μ€νΈ νκ²½ μ€μ μ μ¬μ©ν©λλ€
// μ£Όμ! preset μ§μ ν μλμ κ°μ΄ κ°κ° λ€μ μ€μ νλ κ²½μ°, μλ‘ μ€μ ν λ΄μ©μΌλ‘ μ μ©λ©λλ€
preset: "@vue/cli-plugin-unit-jest",
moduleFileExtensions: [
'js',
'json',
// λͺ¨λ vue νμΌ(`*.vue`)μ μ²λ¦¬νκΈ° μν΄ Jestμκ² μλ €μ€λλ€
'vue',
],
transform: {
// `vue-jest`λ₯Ό μ¬μ©νμ¬ λͺ¨λ vue νμΌ(`*.vue`)μ μ²λ¦¬ν©λλ€
'.*\\.(vue)$': 'vue-jest',
// `babel-jest`λ₯Ό μ¬μ©νμ¬ λͺ¨λ js νμΌ(`*.js`)μ μ²λ¦¬ν©λλ€
'.*\\.(js)$': 'babel-jest',
},
moduleNameMapper: {
// λ³μΉ @(νλ‘μ νΈ/src) μ¬μ©νμ¬ νμ κ²½λ‘μ νμΌμ 맡νν©λλ€
'^@/(.*)$': '<rootDir>/src/$1'
},
testMatch: [
// __tests__ κ²½λ‘ νμμ μλ λͺ¨λ js/ts/jsx/tsx νμΌμ ν
μ€νΈ λμμΌλ‘ μ§μ ν©λλ€
'**/__tests__/**/*.[jt]s?(x)',
// 'xxx.spec' λλ 'xxx.test'λΌλ μ΄λ¦μ λͺ¨λ js/ts/jsx/tsx νμΌμ ν
μ€νΈ λμμΌλ‘ μ§μ ν©λλ€
'**/?(*.)+(spec|test).[jt]s?(x)'
],
// node_modules κ²½λ‘ νμμ μλ λͺ¨λ ν
μ€νΈ νμΌμ λμμμ μ μΈν©λλ€
testPathIgnorePatterns: ['/node_modules/'],
collectCoverage: true,
collectCoverageFrom: [
'**/*.{js,vue}',
'!**/node_modules/**'
],
};
TIP
jest.config.js
νμΌλ‘ λΆλ¦¬νλ©΄ νκ²½ μ€μ λΆλΆλ§ λͺ¨μλμ μ μμ΄μ μ μ§λ³΄μκ° μ¬μμ§λλ€. μλ°μ€ν¬λ¦½νΈ νμΌμ΄λ―λ‘ μ£Όμ μμ±λ κ°λ₯ν©λλ€.
κ° μ€μ μ λν μμΈ μ€λͺ
preset
- μ΄λ―Έ ν μ€νΈμ νμν κΈ°λ³Έ μ€μ μ κ°μΆ μΈλΆ νκ²½ μ€μ μ μ¬μ©ν©λλ€.
@vue/cli-plugin-unit-jest
μ κ²½μ° vue-cli νκ²½μ λ§μΆ° κΈ°λ³Έμ μΌλ‘ μΈν λ ν μ€νΈ νκ²½ μ€μ μ μ¬μ©ν©λλ€.- κΈ°λ³Έμ μΌλ‘ μΈν νλ λ΄μ©μ vue-cli github (opens new window)μμ νμΈν΄λ³΄μΈμ.
moduleFileExtensions
- λͺ¨λμμ μ¬μ©ν νμΌ νμ₯λͺ μ μ§μ ν©λλ€.
transform
- λ³νκΈ°(transformer, μ°μΈ‘)λ₯Ό μ¬μ©νμ¬ μ§μ ν λμ(μ’μΈ‘)μ λ³νν©λλ€.
moduleNameMapper
- λͺ¨λ μ΄λ¦(μ°μΈ‘)μ νΉμ μ΄λ¦(μ’μΈ‘)μΌλ‘ 맡ννμ¬ μΉνν©λλ€.
'^@/(.*)$': '<rootDir>/src/$1'
μμ$1
μ μ’μΈ‘ μ κ· ννμ(Regular Expression) μ€(.*)
μ μΉνλμ΄,<rootDir>/src/
νμμ λͺ¨λ νμΌμ κ°λ¦¬ν΅λλ€.
testMatch
- ν μ€νΈ μ€ν μ λμμΌλ‘ ν¬ν¨μν¬ νμΌμ μ§μ ν©λλ€.
testPathIgnorePatterns
testMatch
μμ μ§μ ν λμ μ€ μλμ μΌλ‘ ν μ€νΈ λμμμ μ μΈν κ²½λ‘ λλ νΉμ ν¨ν΄μ κ°μ§ λμμ μ§μ ν©λλ€.node_modules
νμμλ λΌμ΄λΈλ¬λ¦¬ λͺ¨λμ΄ μ€μΉλμ΄ μμΌλ μΌλ°μ μΌλ‘ λμμμ μ μΈν©λλ€.
collectCoverage
- 컀λ²λ¦¬μ§ μ 보μ μμ§ μ¬λΆλ₯Ό μ€μ ν©λλ€.
- ν
μ€νΈ 컀λ²λ¦¬μ§λ₯Ό λνλ΄λ μ§ν λ³΄κ³ μλ₯Ό μμ±ν©λλ€.
true
μΌ κ²½μ°, ν μ€νΈ μ€νν λλ§λ€ μλμΌλ‘ μμ±ν©λλ€.false
μΌ κ²½μ°, μ μ€νΈλ₯Ό λͺ λ Ήμ΄λ‘ μ€νν λ--coverage
λ₯Ό ν¬ν¨ν΄μ£Όλ©΄ μμ±ν©λλ€.
- κ²°κ³Όλ
ν°λ―Έλ
λλνλ‘μ νΈ/lcov-report/index.html
μ μ΄μ΄λ³΄λ©΄ νμΈν μ μμ΅λλ€.
collectCoverageFrom
- 컀λ²λ¦¬μ§λ₯Ό μμ§ν λμμ μ§μ ν©λλ€.
- μ μ€μ μ κ²½μ°, λͺ¨λ js νμΌκ³Ό vue νμΌμ λνμ¬ μμ§νλ©° node_modulesμ μ‘΄μ¬νλ νμΌμ λμμμ μ μΈν©λλ€.
μμΈν λ΄μ©μ Jest 곡μ λ¬Έμμ Configuring Jest (opens new window)μμ νμΈν΄λ³΄μΈμ.
μ΄μ λ€μ μ±ν°μμ κ°λ¨ν ν μ€νΈ μ½λλ₯Ό μμ±ν΄λ³΄κ² μ΅λλ€.