使用Dockerfile运行Jest测试

6

问题

嘿,我没怎么用过 Docker - 我试图通过 Dockerfile 运行我的 Jest 测试。但是,在尝试构建镜像时,我遇到了以下错误:

错误信息

Step 13/16 : RUN if [ "$runTests" = "True" ]; then     RUN npm test; fi
 ---> Running in ccdb3f89fb79
/bin/sh: RUN: not found

Dockerfile

FROM node:10-alpine as builder
ARG TOKEN
WORKDIR /app

ARG runTests


COPY .npmrc-pipeline .npmrc

COPY package*.json ./
RUN npm install
COPY . .

RUN rm -f .npmrc

ENV PORT=2000
ENV NODE_ENV=production


RUN if [ "$runTests" = "True" ]; then \
    RUN npm test; fi

RUN npm run build

EXPOSE 2000

CMD ["npm", "start"]

我使用的命令来构建镜像是这个,目的是只有在runTests=True时才能运行测试。
docker build -t d-image --build-arg runTests="True" --build-arg "MY TOOOOOKEN"

仅使用Dockerfile是否可以实现这一点?还是必须同时使用docker-compose呢?

条件语句似乎工作得很好。

不可能有两个CMD命令

我尝试了以下解决方法(但它没有起作用): Dockerfile

FROM node:10-alpine as builder
ARG TOKEN
WORKDIR /app

ARG runTests


COPY .npmrc-pipeline .npmrc

COPY package*.json ./
RUN npm install
COPY . .

RUN rm -f .npmrc

ENV PORT=3000
ENV NODE_ENV=production


RUN npm run build

EXPOSE 3000

CMD if [ "$runTests" = "True" ]; then \
    CMD ["npm", "test"] && ["npm", "start"] ;fi

目前我没有从测试中得到任何输出,但它似乎是成功的。

进展

我已经有了一些进展,当我构建镜像时,实际上测试正在运行。我还决定使用RUN命令来运行测试,这样它们就可以在构建步骤中运行。

Dockerfile:

FROM node:10-alpine as builder
ARG TOKEN
WORKDIR /app


COPY .npmrc-pipeline .npmrc

COPY package*.json ./
RUN npm install
COPY . .
RUN rm -f .npmrc
ENV PORT=3000
ENV NODE_ENV=production
RUN npm run build
RUN npm test
EXPOSE 3000

错误:

FAIL src/pages/errorpage/tests/accessroles.test.jsx
   Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
      If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/en/ecmascript-modules for how to enable it.
      To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
      If you need a custom transformation specify a "transform" option in your config.
      If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

我认为docker build过程在Dockerfile中拷贝并安装package.json文件后,似乎没有使用其中的jest:{...}配置。有何想法?


我会选择CMD,就像在这里指出的那样:https://dev59.com/EVIH5IYBdhLWcg3wVcZD - sleepwalker
这个回答解决了你的问题吗?使用外部参数的 Dockerfile if else 条件 - mrak
我已经在帖子中成功使用了条件语句,但还是谢谢。 - meerkat
@sleepwalker 的问题是每个 Dockerfile 只能有一个 CMD 命令。问题已经更新。 - meerkat
看起来你在shell的if ... then ... fi结构中重复了Docker的RUN/CMD行。你不需要这样做,只需删除第二个RUN/CMD即可。(构建步骤将无条件运行,但如果测试失败,则不会执行任何操作。) - David Maze
2个回答

8

RUNCMD不是命令,它们是指示Docker在构建容器时要执行的操作。例如:

RUN if [ "$runTests" = "True" ]; then \
    RUN npm test; fi

这不太合理,RUN <command> 运行一个 shell 命令但是 RUN 在 shell 中没有被定义,应该改为:

ARG runTests  # you need to define the argument too
RUN if [ "$runTests" = "True" ]; then \
    npm test; fi

更加规范的做法是将 npm 设置为入口点,而 start 则作为特定的命令:

ENTRYPOINT [ "npm" ]
CMD [ "start" ]

这允许您正常构建容器,不需要任何构建参数,然后在容器中运行除start以外的NPM脚本,例如运行npm test

docker run <image> test

需要注意的是,这意味着所有的开发依赖项都需要在容器中。从ENV NODE_ENV=production可以看出,您打算进行生产构建,因此不应该在容器中运行测试。即使使用了as builder,这也不是真正的多阶段构建。这种情况下最惯用的脚本可能类似于:

# stage 1: copy the source and build the app

FROM node:10-alpine as builder
ARG TOKEN

WORKDIR /app

COPY .npmrc-pipeline .npmrc

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# stage 2: copy the output and run it in production

FROM node:10-alpine

WORKDIR /app

ENV PORT=3000
ENV NODE_ENV=production

COPY --from=builder /app/package*.json ./
RUN npm ci

COPY --from=builder /* your build output */

EXPOSE 3000

ENTRYPOINT [ "npm" ]
CMD [ "start" ]

请看我为全栈React/Express应用程序准备的这个Dockerfile


为什么要再次运行 RUN npm ci 而不是在阶段之间复制 node modules? - OneCricketeer
@OneCricketeer 因为您不希望在最终容器中包含所有的开发依赖项;我想您可以将整个目录复制,然后代替此操作 prune - jonrsharpe
没错,我也是这么想的,但是我认为这可能会在 Docker 层中导致额外的存储,因为 RUN npm prune 会比 COPY --from=builder node_modules 新建一个层... 我还没有真正测试过,但正在重构一些 NodeJS 项目。 - OneCricketeer

0

关于这个问题,有几点需要注意:

虽然我在这里使用了两个命令(但不建议这样做),如下:https://hub.docker.com/repository/docker/djangofan/mountebank-with-ui-node

此外,我不建议在构建容器镜像时运行测试。相反,应该构建容器镜像,使其将文件夹映射到测试文件的位置。然后,在compose文件中包含您的“临时测试镜像”。

version: '3.4'
services:
  service-api:
    container_name: service-api
    build:
      context: .
      dockerfile: Dockerfile-apibase
    ports:
      - "8083:8083"
  e2e-tests:
    container_name: e2e-tests
    build:
      context: .
      dockerfile: Dockerfile-testbase
    command: bash -c "wait-for-it.sh service-api:8083 && gradle -q clean test -Dorg.gradle.project.buildDir=/usr/src/example"

然后这样执行,以获得0或1的退出码:
docker-compose up --exit-code-from e2e-tests

运行后,服务将保持运行状态,但测试将在完成后关闭。

希望这样说得通,即使我给出的例子并不完全像你的情况。这里是我之前示例的链接,你可以自己尝试一下。对于Jest测试,它应该能够工作得类似。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接