建议的在Docker容器中执行测试的方式

3
我想在Docker容器中运行一组特定的测试,但不确定如何解决。我想要执行的测试与安全相关,例如创建用户,为他们管理GPG密钥等 - 我不想在运行测试的计算机上运行这些测试。
我尝试了pytest-xdist/socketserver组合以及将测试复制到正在运行的Docker容器中,并使用pytest-json-report将结果保存为json并与主机共享卷,但不确定这种方法是否好。
目前,我希望所有测试(没有标记或其他功能)都在“远程”(在Docker中)执行,并且结果呈现为在本地计算机上运行的一样。
我不介意编写一个特定的插件,但不确定这是否是一个好方法:我必须确保我的插件在pytest-xdist(或其他一些插件)之前加载吗?此外,如果我在我的conftest.py中使用pytest_sessionstart来构建Docker镜像,然后用xdist进行目标测试;但我的测试还有一些“依赖项”,我必须将它们放在conftest.py中 - 我不能在容器和运行测试的个人计算机上使用同一个conftest.py。
提前感谢您。

如果你想避免直接调用docker来运行这些测试的话,那么使用一个运行脚本是可行的。 - EDG956
许多CI工具允许您在docker环境中定义步骤,其中您只需像在正常主机(物理或虚拟,但不是docker)上运行命令一样发出命令。在这种情况下,您只需运行pytest并知道它将在从您构建的镜像生成的容器内执行。然后,您可能希望拥有一个通用的entrypoint,以便您可以像在shell中一样使用相同的命令(CMD)。 - EDG956
使用 entrypointcmd 应该能让你对我所说的有一些了解。 - EDG956
我认为你可以有几种实现方法。从我的经验来看,我认为你可以在本地使用一个不同名称的conftest覆盖docker中的conftest,但是具体细节我无法记起。你最好提出一个新问题询问。 - EDG956
我知道这有点脏,但你可以使用类似 docker run --rm --entrypoint=pytest <your_docker_image> 这样的东西。 - undefined
显示剩余6条评论
1个回答

1
如果有其他人可能有类似的需求,我会分享我所做的事情。
首先,已经有一个出色的 pytest-json-report 来导出JSON结果。然而,我制作了一个更简单、功能更少的插件,直接使用 pytest_report_to_serializable
import json
from socket import gethostname


def pytest_addoption(parser, pluginmanager):
    parser.addoption(
        '--report-file', default='%s.json' % gethostname(), help='path to JSON report'
    )


def pytest_configure(config):
    plugin = JsonTestsExporter(config=config)
    config._json_report = plugin
    config.pluginmanager.register(plugin)


def pytest_unconfigure(config):
    plugin = getattr(config, '_json_report', None)
    if plugin is not None:
        del config._json_report
        config.pluginmanager.unregister(plugin)

    print('Report saved in: %s' % config.getoption('--report-file'))


class JsonTestsExporter(object):

    def __init__(self, config):
        self._config = config
        self._export_data = {'collected': 0, 'results': []}

    def pytest_report_collectionfinish(self, config, start_path, startdir, items):
        self._export_data['collected'] = len(items)

    def pytest_runtest_logreport(self, report):
        data = self._config.hook.pytest_report_to_serializable(
            config=self._config, report=report
        )
        self._export_data['results'].append(data)

    def pytest_sessionfinish(self, session):
        report_file = self._config.getoption('--report-file')
        with open(report_file, 'w+') as fd:
            fd.write(json.dumps(self._export_data))

原因是我想要使用pytest_report_from_serializable导入结果。
简化的Dockerfile:
FROM debian:buster-slim AS builder

COPY [ "requirements.txt", "run.py", "/artifacts/" ]
COPY [ "json_tests_exporter", "/artifacts/json_tests_exporter/" ]

RUN apt-get update\
# install necesssary packages
 && apt-get install --no-install-recommends -y python3-pip python3-setuptools\
# build json_tests_exporter *.whl
 && pip3 install wheel\
 && sh -c 'cd /artifacts/json_tests_exporter && python3 setup.py bdist_wheel'

FROM debian:buster-slim

ARG USER_UID=1000
ARG USER_GID=${USER_UID}

COPY --from=builder --chown=${USER_UID}:${USER_GID} /artifacts /artifacts

RUN apt-get update\
# install necesssary packages
 && apt-get install --no-install-recommends -y wget gpg openssl python3-pip\
# create user to perform tests
 && groupadd -g ${USER_GID} pytest\
 && adduser --disabled-password --gecos "" --uid ${USER_UID} --gid ${USER_GID} pytest\
# copy/install entrypoint script and preserver permissions
 && cp -p /artifacts/run.py /usr/local/bin/run.py\
# install required Python libraries
 && su pytest -c "pip3 install -r /artifacts/requirements.txt"\
 && su pytest -c "pip3 install /artifacts/json_tests_exporter/dist/*.whl"\
# make folder for tests and results
 && su pytest -c "mkdir -p /home/pytest/tests /home/pytest/results"

VOLUME [ "/home/pytest/tests", "/home/pytest/results" ]

USER pytest

WORKDIR /home/pytest/tests

ENTRYPOINT [ "/usr/local/bin/run.py" ]

JSON导出插件位于与Dockerfile相同的文件夹中。
run.py非常简单:
#!/usr/bin/python3

import pytest
import sys
from socket import gethostname


def main():
    if 1 == len(sys.argv):
        # use default arguments
        args = [
            '--report-file=/home/pytest/results/%s.json' % gethostname(),
            '-qvs',
            '/home/pytest/tests'
            ]
    else:
        # caller passed custom arguments
        args = sys.argv[1:]

    try:        
        res = pytest.main(args)
    except Exception as e:
        print(e)
        res = 1

    return res


if __name__ == "__main__":
    sys.exit(main())

requirements.txt 只包含:
python-gnupg==0.4.4
pytest>=7.1.2

基本上,我可以用以下方式运行所有东西:
docker build -t pytest-runner ./tests/docker/pytest_runner
docker run --rm -it -v $(pwd)/tests/results:/home/pytest/results -v $(pwd)/tests/fixtures:/home/pytest/tests pytest-runner

我可以帮您翻译成中文:

最后两行是我使用 Docker API 在 Python 中通过 pytest_sessionstart(session) 钩子程序自动运行的。


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