Jest: Jest๋Š” CSS๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” JSX์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2017๋…„ 03์›” 07์ผ  ยท  24์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: facebook/jest

๊ธฐ๋Šฅ ์„ ์š”์ฒญํ•˜๊ฑฐ๋‚˜ ๋ฒ„๊ทธ ๋ฅผ ๋ณด๊ณ ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?
๋ฒŒ๋ ˆ

ํ˜„์žฌ ํ–‰๋™์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?
app.jsx ์— import './app.css'; ๊ฐ€ ์žˆ์œผ๋ฉด ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ๋‹ค์Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

yarn run:test v0.21.3
$ NODE_ENV=client jest -u
 FAIL  src/__tests__/app.test.js
  โ— Test suite failed to run

    /home/svipben/Documents/react-boilerplate/src/client/app/app.css:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){body {
                                                                                                  ^
    SyntaxError: Unexpected token {

      at transformAndBuildScript (node_modules/jest-runtime/build/transform.js:320:12)
      at Object.<anonymous> (src/client/app/app.jsx:7:1)
      at Object.<anonymous> (src/__tests__/app.test.js:2:12)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.693s
Ran all test suites.
error Command failed with exit code 1.

์˜ˆ์ƒ๋˜๋Š” ๋™์ž‘์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?
์˜ค๋ฅ˜ ์—†์ด ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ ค๋ฉด...

์ •ํ™•ํ•œ Jest ๊ตฌ์„ฑ์„ ์ œ๊ณตํ•˜๊ณ  Jest, ๋…ธ๋“œ, yarn/npm ๋ฒ„์ „ ๋ฐ ์šด์˜ ์ฒด์ œ๋ฅผ ์–ธ๊ธ‰ํ•˜์‹ญ์‹œ์˜ค.

OS: ๋ฆฌ๋ˆ…์Šค ์šฐ๋ถ„ํˆฌ 16.04
๋†๋‹ด: ์ตœ์‹ 
์›์‚ฌ: ์ตœ์‹ 
๋…ธ๋“œ: ์ตœ์‹ 
babel-jest : ์ตœ์‹ 

.babelrc:

        "client": {
            "presets": [
                "latest", // All you need to compile what's in ES2015+
                "flow", // Flow support
                "react", // Strip flow types and transform JSX
                "stage-2" // Experimental syntax extensions
            ],
            "sourceMaps": true // Compile with Source Maps
        },

๋†๋‹ด: NODE_ENV=ํด๋ผ์ด์–ธํŠธ ๋†๋‹ด -u

์ถ”์‹  import './app.css'; ์— ๋Œ“๊ธ€์„ ๋‹ฌ๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•˜๋ฉด ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

package.json ํŒŒ์ผ์˜ jest ๊ตฌ์„ฑ์—์„œ moduleNameMapper ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

{
   "jest":{
        "moduleNameMapper":{
             "\\.(css|less|sass|scss)$": "<rootDir>/__mocks__/styleMock.js",
             "\\.(gif|ttf|eot|svg)$": "<rootDir>/__mocks__/fileMock.js"
        }
   }
}

๊ทธ๋Ÿฐ ๋‹ค์Œ ์•„๋ž˜ ์„ค๋ช…๋œ ๋Œ€๋กœ ๋‘ ๊ฐœ์˜ ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • __mocks__/styleMock.js
module.exports = {};
  • __mocks__/fileMock.js
module.exports = 'test-file-stub';

CSS ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ className ์กฐํšŒ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋ก์‹œ๋ฅผ ์กฐ๋กฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ๊ตฌ์„ฑ์ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.

{
  "jest":{
     "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
      "\\.(css|less|scss|sass)$": "identity-obj-proxy"
    },
  }
}

๊ทธ๋Ÿฌ๋‚˜ ๊ฐœ๋ฐœ์ž ์ข…์†์„ฑ์œผ๋กœ identity-obj-proxy ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

yarn add identity-obj-proxy -D

์ž์„ธํ•œ ๋‚ด์šฉ์€. jest ๋ฌธ์„œ ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  24 ๋Œ“๊ธ€

@maximderbin ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

@maximderbin ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! moduleNameMapper ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋‚ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

@svipben @maximderbin @simonxl ์ด moduleNameMapper๋ฅผ ์–ด๋””์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜๋‚˜์š”?
๋‚˜๋Š” ๋ฌธ์„œ์—์„œ ์šฐ๋ฆฌ๊ฐ€ ๊ทธ๊ฒƒ์„ packag.json์— ์ถ”๊ฐ€ํ•  ๊ฒƒ์ด์ง€๋งŒ ์–ด๋Š ๊ฒƒ์„ ๋ณผ ๊ฒƒ์ธ๊ฐ€?
tnx

@sarahmarciano moduleNameMapper ๋Š” ๊ตฌ์„ฑ์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด "jest" ํ‚ค ์•„๋ž˜์˜ package.json(์ข…์†์„ฑ์—์„œ jest ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ๊ฒƒ)์˜ ๊ตฌ์„ฑ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{
  "dependencies": {...}
  "jest": {
    "moduleNameMapper": {...}
  }
}

package.json ํŒŒ์ผ์˜ jest ๊ตฌ์„ฑ์—์„œ moduleNameMapper ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

{
   "jest":{
        "moduleNameMapper":{
             "\\.(css|less|sass|scss)$": "<rootDir>/__mocks__/styleMock.js",
             "\\.(gif|ttf|eot|svg)$": "<rootDir>/__mocks__/fileMock.js"
        }
   }
}

๊ทธ๋Ÿฐ ๋‹ค์Œ ์•„๋ž˜ ์„ค๋ช…๋œ ๋Œ€๋กœ ๋‘ ๊ฐœ์˜ ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • __mocks__/styleMock.js
module.exports = {};
  • __mocks__/fileMock.js
module.exports = 'test-file-stub';

CSS ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ className ์กฐํšŒ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋ก์‹œ๋ฅผ ์กฐ๋กฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ๊ตฌ์„ฑ์ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.

{
  "jest":{
     "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
      "\\.(css|less|scss|sass)$": "identity-obj-proxy"
    },
  }
}

๊ทธ๋Ÿฌ๋‚˜ ๊ฐœ๋ฐœ์ž ์ข…์†์„ฑ์œผ๋กœ identity-obj-proxy ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

yarn add identity-obj-proxy -D

์ž์„ธํ•œ ๋‚ด์šฉ์€. jest ๋ฌธ์„œ ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๊ถŒ์žฅํ•˜๋Š” ์ ‘๊ทผ ๋ฐฉ์‹์€ CSS ๋ชจ๋“ˆ์ด JSX ํŒŒ์ผ์ด ์˜์กดํ•˜๋Š” ๊ฐ’์„ ๋‚ด๋ณด๋‚ด๋Š” ๊ฒฝ์šฐ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ๊ฐ€์ด๋“œ๋Š” ํด๋ž˜์Šค ์ด๋ฆ„์„ ๋‚ด๋ณด๋‚ด๋Š” ๋ฐ CSS ๋ชจ๋“ˆ๋งŒ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. CSS ๋ชจ๋“ˆ์„ ์ œ๋Œ€๋กœ ์กฐ๋กฑํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ€์ด๋“œ๊ฐ€ ์žˆ์—ˆ์œผ๋ฉด ํ•ฉ๋‹ˆ๋‹ค.

@matthew-dean "CSS ๋ชจ๋“ˆ์„ ์ ์ ˆํ•˜๊ฒŒ ์กฐ๋กฑ"ํ•œ๋‹ค๋Š” ๊ฒƒ์€ ๋ฌด์—‡์„ ์˜๋ฏธํ•ฉ๋‹ˆ๊นŒ? ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์กฐ๊ธˆ ๋” ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? identity-obj-proxy ๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒฝํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.

:export {
  sizeMobile: $size-mobile / 1px;
  sizePhablet: $size-phablet / 1px;
  sizeSmallTablet: $size-small-tablet / 1px;
  sizeTablet: $size-tablet / 1px;
  sizeDesktop: $size-desktop / 1px;
  sizeLargeDesktop: $size-large-desktop / 1px;
}
import styles from './variables.scss'

const tileSize = parseFloat(styles.sizeTablet) / 3
//...
render() {
  return <Tile style={{width: tileSize}} />
}

์ด๊ฒƒ์€ CSS ๋ชจ๋“ˆ๊ณผ JS ๊ฐ„์— ์ƒ์ˆ˜(๊ฐ’)๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๊ณ  ๊ฐ’์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง ๋˜๋Š” ๋™์  props๋ฅผ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Œ์„ ๋ณด์—ฌ์ฃผ๋Š” ์ธ์œ„์ ์ธ ์˜ˆ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ identity-obj-proxy ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์กด์žฌํ•˜๋Š” ์‹ค์ œ ๊ฐ’์€ ๊ณ ์‚ฌํ•˜๊ณ  ์ด๋Ÿฌํ•œ ๊ฐ’์„ ์ •์ˆ˜๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ CSS ๋ชจ๋“ˆ์— ์ •์˜๋œ ์ƒ์ˆ˜์— ์˜์กดํ•˜๋Š” JSX ์ฝ”๋“œ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ CSS ๋ชจ๋“ˆ์„ ์ ์ ˆํ•˜๊ฒŒ ์กฐ๋กฑํ•˜์ง€ ์•Š๊ณ ๋Š” ์ด๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค(๊ฐ’์ด ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•œ ์ •์ˆ˜๋กœ ๋ฐ˜ํ™˜๋œ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ง). identity-obj-proxy ๋Š” CSS ๋ชจ๋“ˆ์— ๋ฒ”์œ„๊ฐ€ ์ง€์ •๋œ CSS ํด๋ž˜์Šค ์ด๋ฆ„๋งŒ ์žˆ๋Š” ๊ฐ์ฒด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ JS ์ž‘์—…์—์„œ ์ฐจ๋ก€๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ’/์ƒ์ˆ˜๋ฅผ ๋‚ด๋ณด๋‚ด๋Š” ๊ธฐ๋Šฅ์ธ CSS ๋ชจ๋“ˆ์˜ ๋‹ค๋ฅธ ์ธก๋ฉด์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@SimenB

๋ง๋ถ™์—ฌ์„œ, ๋‚˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด CSS ๋ชจ๋“ˆ์„ ์„ฑ๊ณต์ ์œผ๋กœ ์กฐ๋กฑํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

const styles = require('styles/variables.scss')

jest.mock('styles/variables.scss', () => {
  return {
    sizePhablet: 500,
    sizeSmallTablet: 768,
    sizeTablet: 1024,
    sizeDesktop: 1440,
    sizeLargeDesktop: 1920
  }
})

CSS ๋ชจ๋“ˆ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ณ  ๋‚˜์ฒ˜๋Ÿผ ๊ธฐ๋ณธ " identity-obj-proxy ์‚ฌ์šฉ" ์กฐ์–ธ์„ ๋ฌด์‹œํ•˜๊ณ  ์žˆ๋˜ ๋‹ค๋ฅธ ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ์ด๊ฒƒ์ด ์œ ์šฉํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

'jest-transform-css'๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.
์„ค์น˜ ํ›„ jest config.xml์— ์•„๋ž˜๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

"transform": {
      "^.+\\.js$": "babel-jest",
      ".+\\.(css|styl|less|sass|scss)$": "jest-transform-css"
    }

์ด์ œ css ํŒŒ์ผ์€ js ํŒŒ์ผ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํŠธ๋žœ์ŠคํŒŒ์ผ๋ฉ๋‹ˆ๋‹ค.

@matthew-dean ๋ชจ์˜ CSS ๋ชจ๋“ˆ์„ ์–ด๋””์—์„œ ์ž‘์„ฑํ–ˆ์œผ๋ฉฐ jest์— ์–ด๋–ป๊ฒŒ ์ฐธ์กฐํ•ฉ๋‹ˆ๊นŒ?

์™ธ๋ถ€ ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž์ฒด ํŒŒ์ผ์„ ๋‹จ์ผ ํŒŒ์ผ๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
<strong i="6">@import</strong> "progress-tracker/progress-tracker-variables.scss";

ํ…Œ์ŠคํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด Jest๋ฅผ ์‹คํ–‰ํ•  ๋•Œ๋งˆ๋‹ค
image
์œ„์˜ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๋ฏธ package.json ์•ˆ์— css ๋ชจํ˜•์„ ์ง€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
image

ํ•ด๋‹น ๋ฌธ์ œ์— ๋Œ€ํ•ด ๋„์›€์„ ๋ฐ›๊ธฐ๋ฅผ ๊ณ ๋Œ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ํŠธ๋ฆญ, ์ œ์•ˆ?

ํŽธ์ง‘: babel-jest ๋ฐ identity-obj-proxy ํŒจํ‚ค์ง€๋ฅผ ๋ชจ๋‘ ์‹œ๋„ํ–ˆ์ง€๋งŒ ๊ทธ ์ค‘ ์•„๋ฌด ๊ฒƒ๋„ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” ์—ฌ์ „ํžˆ ์ง€์†๋ฉ๋‹ˆ๋‹ค.

ํŽธ์ง‘ 2: ๋˜ํ•œ ์›น์—์„œ ๋ณธ ์†”๋ฃจ์…˜์— ๋”ฐ๋ผ ์•„๋ž˜ ๊ตฌ์„ฑ์„ ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค.
image

๋‚˜๋Š” Babel 7 , babel-jest ^24.5.0 , Jest ^24.1.0 ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„์˜ ์กฐ์–ธ์„ ๋”ฐ๋ฅธ ํ›„์—๋„ ๋Œ€์ƒ ํŒŒ์ผ(์˜ˆ: Post.test.js import Post.js ) CSS ํŒŒ์ผ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค(์˜ˆ: ./postStyles.css .

Jest encountered an unexpected token

SyntaxError: Unexpected token .

Details:

    /Users/ben/Desktop/Work/code/bengrunfeld/src/front-end/components/Post/postStyles.css:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){.post h1, .post h2, .post h3 {

      3 |
      4 | import { PostText } from './styles'
    > 5 | import './postStyles.css'

์œ„์— ๋งํฌ๋œ ๊ธฐ์‚ฌ์— ๋”ฐ๋ฅด๋ฉด ์ด๊ฒƒ์€ package.json ์— ์žˆ์Šต๋‹ˆ๋‹ค.

"jest": {
    "moduleFileExtensions": ["js", "jsx"],
    "moduleDirectories": ["node_modules", "bower_components", "shared"],

    "moduleNameMapper": {
      "\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js",
      "\\.(gif|ttf|eot|svg)$": "<rootDir>/__mocks__/fileMock.js",

      "^react(.*)$": "<rootDir>/vendor/react-master$1",
      "^config$": "<rootDir>/configs/app-config.js"
    }
  },

๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์—๋Š” ๋‹ค์Œ ์ฝ”๋“œ๊ฐ€ ์žˆ๋Š” __mocks__/styleMock.js ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

module.exports = {};

๋งˆ์ง€๋ง‰์œผ๋กœ ๋‚ด jest.config.js :

module.exports = {
  setupFiles: ['<rootDir>/src/test/setup.js']
}

์ฐจ๋ก€๋กœ Enzyme์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

import Enzyme from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'

Enzyme.configure({ adapter: new Adapter() })

import Error
๋‚ด scs ํŒŒ์ผ์— @import ๋ฌธ์ด ํฌํ•จ๋œ ๊ฑฐ์˜ ๋™์ผํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊ณ ๊ตฐ๋ถ„ํˆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. SyntaxError: ์œ ํšจํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ํ† ํฐ์„ ์•Œ๋ ค์ฃผ๋Š” ํ…Œ์ŠคํŠธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€ ๋‚˜๋ฅผ ๋„์™€์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์œ„์— ์ œ๊ณต๋œ ์†”๋ฃจ์…˜์„ ๋”ฐ๋ž์ง€๋งŒ ๊ทธ ์ค‘ ์–ด๋Š ๊ฒƒ๋„ ๋‚˜๋ฅผ ์œ„ํ•ด ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

@babel/polyfill ๋ฅผ ๊ฐœ๋ฐœ ์ข…์†์„ฑ์œผ๋กœ ์ถ”๊ฐ€ํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

@fthomas82๋„ ๋งˆ์ฐฌ๊ฐ€์ง€

package.json ํŒŒ์ผ์˜ jest ๊ตฌ์„ฑ์—์„œ moduleNameMapper ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

{
   "jest":{
        "moduleNameMapper":{
             "\\.(css|less|sass|scss)$": "<rootDir>/__mocks__/styleMock.js",
             "\\.(gif|ttf|eot|svg)$": "<rootDir>/__mocks__/fileMock.js"
        }
   }
}

๊ทธ๋Ÿฐ ๋‹ค์Œ ์•„๋ž˜ ์„ค๋ช…๋œ ๋Œ€๋กœ ๋‘ ๊ฐœ์˜ ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • __mocks__/styleMock.js
module.exports = {};
  • __mocks__/fileMock.js
module.exports = 'test-file-stub';

CSS ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ className ์กฐํšŒ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋ก์‹œ๋ฅผ ์กฐ๋กฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ๊ตฌ์„ฑ์ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.

{
  "jest":{
     "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
      "\\.(css|less|scss|sass)$": "identity-obj-proxy"
    },
  }
}

๊ทธ๋Ÿฌ๋‚˜ ๊ฐœ๋ฐœ์ž ์ข…์†์„ฑ์œผ๋กœ identity-obj-proxy ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

yarn add identity-obj-proxy -D

์ž์„ธํ•œ ๋‚ด์šฉ์€. jest ๋ฌธ์„œ ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์€ ๋‚˜๋ฅผ ์œ„ํ•ด ์ผํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌ ํ•ด์š”!!!

'jest-transform-css'๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.
์„ค์น˜ ํ›„ jest config.xml์— ์•„๋ž˜๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

"transform": {
      "^.+\\.js$": "babel-jest",
      ".+\\.(css|styl|less|sass|scss)$": "jest-transform-css"
    }

์ด์ œ css ํŒŒ์ผ์€ js ํŒŒ์ผ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํŠธ๋žœ์ŠคํŒŒ์ผ๋ฉ๋‹ˆ๋‹ค.

๊ณ ๋งˆ์›Œ, ํ˜• @krajasekhar
๊ทธ๊ฒƒ์€ ๋‚˜๋ฅผ ์œ„ํ•ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค ๐Ÿ‘

๋‚˜๋Š” packages.json ์— ์ œ์•ˆ๋œ ๊ตฌ์„ฑ์„ ์ถ”๊ฐ€ํ–ˆ์ง€๋งŒ ์„ฑ๊ณตํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ œ ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— jest.config.js ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค. Jest๋Š” packages.json ์ด์ƒ์˜ ๊ตฌ์„ฑ ํŒŒ์ผ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ ์—ฌ๊ธฐ ๋‚ด jest.config.js ํŒŒ์ผ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

module.exports = {
  setupFilesAfterEnv: ['<rootDir>/.setup-tests.js'],
  testPathIgnorePatterns: ['<rootDir>/.next/', '<rootDir>/node_modules/'],
  transform: {
    '^.+\\.js$': 'babel-jest',
    '.+\\.(css|styl|less|sass|scss)$': 'jest-transform-css',
  },
};

๋‚ด ์ฝ”๋“œ์—์„œ ์›์‹œ CSS๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค.

import "./cloud.css?raw"

๋‚˜๋Š” ๋˜ํ•œ jest ๊ตฌ์„ฑ์„ ์ˆ˜์ •ํ–ˆ๋‹ค.
"jest":{ "moduleNameMapper": { "\\.(css|less|scss|sass|css?raw)$": "identity-obj-proxy" },

๊ทธ๋ž˜๋„ ๊ฐ€์ ธ์˜ค๊ธฐ ๋ผ์ธ์—์„œ ํ…Œ์ŠคํŠธ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์›์‹œ ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์กฐ๋กฑํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์•„์ด๋””์–ด๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

@import ์˜ˆ๊ธฐ์น˜ ์•Š์€ ํ† ํฐ์˜ ์†”๋ฃจ์…˜=:)

ํŒจํ‚ค์ง€ ์„ค์น˜:
npm i --save-dev identity-obj-proxy

jest.config.js์— ์ถ”๊ฐ€

module.exports = {
  "moduleNameMapper": {
    "\\.(css|less|scss)$": "identity-obj-proxy"
  }
}

๋ˆ„๊ตฐ๊ฐ€ ์—ฌ๊ธฐ์— ๋„์ฐฉํ•˜๋ฉด:
์ด ๋ชจ๋“ˆ์€ ๋‚ด๊ฐ€ ๋ณธ ๊ฒƒ ์ค‘ ๊ฐ€์žฅ ์—…๋ฐ์ดํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค: https://www.npmjs.com/package/jest-css-modules-transform

transform: { '^.+\\.(js|jsx|ts|tsx)$': '<rootDir>/node_modules/babel-jest', ".+\\.(css|styl|less|sass|scss)$": "jest-css-modules-transform" }, transformIgnorePatterns: [ '/node_modules/', '^.+\\.module\\.(css|sass|scss)$', ], moduleNameMapper: { '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy' },

๋‚˜๋Š” packages.json ์— ์ œ์•ˆ๋œ ๊ตฌ์„ฑ์„ ์ถ”๊ฐ€ํ–ˆ์ง€๋งŒ ์„ฑ๊ณตํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ œ ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— jest.config.js ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค. Jest๋Š” packages.json ์ด์ƒ์˜ ๊ตฌ์„ฑ ํŒŒ์ผ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ ์—ฌ๊ธฐ ๋‚ด jest.config.js ํŒŒ์ผ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

module.exports = {
  setupFilesAfterEnv: ['<rootDir>/.setup-tests.js'],
  testPathIgnorePatterns: ['<rootDir>/.next/', '<rootDir>/node_modules/'],
  transform: {
    '^.+\\.js$': 'babel-jest',
    '.+\\.(css|styl|less|sass|scss)$': 'jest-transform-css',
  },
};

์†”๋ฃจ์…˜์„ ์ œ์‹œํ•œ ๋ชจ๋“  ์‚ฌ๋žŒ๊ณผ ํŠน๋ณ„ํžˆ @Shadoow์—๊ฒŒ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค! ์ผ์ข…์˜ ๋…ผ๋ฆฌ์˜€์œผ๋‚˜... config ํŒŒ์ผ์ด package.json์˜ ์„ค์ •๋ณด๋‹ค ์šฐ์„  ์‚ฌ์šฉ๋œ๋‹ค๋Š” ์ ์„ ๋†“์น˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์š”์ปจ๋Œ€, ์ƒˆ๋กœ ์˜จ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ์ €์—๊ฒŒ ํšจ๊ณผ๊ฐ€ ์žˆ์—ˆ๋˜ ๊ฒƒ์€ ์ปค๋ฎค๋‹ˆํ‹ฐ ๋“œ๋ฆผ ํŒ€์ด ์ œ์‹œํ•œ ์—ฌ๋Ÿฌ ์†”๋ฃจ์…˜์˜ ์กฐํ•ฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

1) jest.config.json ํŒŒ์ผ ์ƒ์„ฑ

touch jest.config.json open jest.config.json

2) jest-transform-css๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

yarn add jest-transform-css -D

3) jest ๊ตฌ์„ฑ ํŒŒ์ผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

module.exports = {
  preset: 'ts-jest/presets/js-with-babel',
  moduleNameMapper: {
    '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)$': './jest.mock.js',
  },
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/assets/'],
  transform: {
    '^.+\\.jsx?$': 'babel-jest',
    '\\.(css|less|scss|sass)$': 'jest-transform-css',
  },
};

4) package.json์„ ์—…๋ฐ์ดํŠธํ•˜์‹ญ์‹œ์˜ค(๋จผ์ € ๋ชจ๋“  JEST ๊ตฌ์„ฑ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ์ƒˆ jest ๊ตฌ์„ฑ ํŒŒ์ผ๋กœ ์ „์†ก). ๊ทธ๋Ÿฌ๋ฉด ๋งˆ๋ฒ•์ด ์—ฌ๊ธฐ์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

"test": "jest --config ./jest.config.js"

Tks ๋ชจ๋‘!

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰