Я хотел бы открыть макет существующего файла теста, применить к нему функцию для теста, но не изменить фактическое содержимое файла - PullRequest
1 голос
/ 02 апреля 2019

У меня есть функция, которая обновляет файл спецификации rpm для дальнейшего использования. у него есть три параметра: имя приложения, версия, которую мы хотим изменить, и место, где файл спецификаций находится в файловой системе.

То, что я не могу сделать, это, скажем, иметь файл tests / files / package_name.spec и проверить, что он корректирует его версию, не изменяя фактический файл спецификации. Я не хочу изменять реальный файл спецификации, потому что это сделает тест бесполезным при втором запуске.

Я попытался заглянуть в библиотеку mock и mock_open, но, похоже, ничего не подходит для этого случая.

def test_update_spec_file():
    update_spec_file("package_name", "5.0.0", "files/package_name")
    with open("files/package_name.spec", "r") as f:
        contents = f.read()
        version_regex = re.compile("^.*define _software_version.*$")
        assert (
            re.search(version_regex, contents).group(0)
            == "%define _software_version 5.0.0"
        )

По сути, функция update_spec_file находит строку %define _software_version 5.0.0, используя regex, и снова использует regex для перехода в новую версию.

Итак, в заключение, можно ли было бы создать файл, открыть его с каким-то имитирующим файлом с его содержимым, запустить функцию и подтвердить, что он выполнил свою работу без фактического изменения файла в файловой системе?

Либо это, либо сбросьте файл после его изменения.

Это код, который обновляет файл спецификации:

def update_spec_file(application, release_version, path_to_spec_file):
    """Update the spec file with the release version.

    Args:
        application (str): Name of the application.
        release_version (str): New version of application.
        path_to_spec_file (str, optional):
            path the the applications spec file we are modifying.
    """
    print(f"Updating spec file _software_version to {release_version}")

    try:
        with open(path_to_spec_file, "r") as spec_file:
            spec_contents = spec_file.readlines()
    except EnvironmentError:
        print(f"Unable to open spec file located at: {path_to_spec_file}")
        # raise the original exception again.
        raise

    # Find and replace software version in contents of spec file.
    version_regex = re.compile("%define _software_version.*?(?=(?:\\n)|$)")
    define_version = f"%define _software_version {release_version}"
    # Splice the new software version line into the existing file contents
    spec_contents[:] = [
        version_regex.sub(define_version, line) for line in spec_contents
    ]

    try:  # Open the file and write the new spliced contents
        with open(path_to_spec_file, "w") as f_spec_file:
            f_spec_file.writelines(spec_contents)
    except EnvironmentError:  # Catch spec file not found.
        print(f"Unable to open spec file located at: {path_to_spec_file}")
        # raise the original exception again.
        raise

редактирование:

это модифицированный код:

def test_update_spec_file():
    content = open("tests/files/package_name.spec").read()
    with patch(__name__ + ".open", create=True) as mock_open:
        mock_open.return_value.__enter__.return_value = StringIO(content)
        with open("tests/files/package_name.spec") as f:
            update_spec_file(
                "package_name", "5.0.0", "tests/files/package_name.spec"

что я делаю, чтобы проверить это cat файл спецификации перед выполнением теста и cat после запуска теста

древовидная структура кода:

release/
├── release_all_in_one
│   ├── changelog_utils.py
│   ├── confluence_email.py
│   ├── confluence_utils.py
│   ├── create_confluence_page.py
│   ├── __init__.py
│   ├── jira_utils.py
│   ├── parse_confluence.py
│   ├── parse_rm_objects.py
│   ├── release_all_in_one_openshift.py
│   ├── release_all_in_one.py
│   ├── tag_branch.py
│   ├── templates
│   │   ├── admin_package_template
│   │   ├── base_template
│   │   ├── cdn_package_template
│   │   ├── fe_package_template
│   │   ├── oddjob_package_template
│   │   ├── openshift_template
│   │   └── procedure.json
│   └── utils.py
├── setup.cfg
├── setup.py
└── tests
    ├── files
    │   └── package_name.spec
    ├── test_changelog.py
    ├── testing_utils.py
    └── test_utils.py

1 Ответ

0 голосов
/ 02 апреля 2019

Вы можете patch функцию open вернуть контекстный менеджер, который возвращает объект io.StringIO с содержимым фактического файла:

from unittest.mock import patch
from io import StringIO
content = '123' # value for demo
# uncomment the line below to read actual file content
# content = open('files/package_name.spec').read()
with patch(__name__ + '.open', create=True) as mock_open:
    mock_open.return_value.__enter__.return_value = StringIO(content)
    with open('files/package_name.spec') as f:
        print(f.read())
        f.seek(0)
        f.write('abc')
        f.seek(0)
        print(f.read())

Это выводит:

123
abc

В качестве альтернативы вы можете назначить open a MagicMock объект, чтобы все ссылки на open в одном и том же модуле были переопределены:

from unittest.mock import MagicMock
from io import StringIO
content = '123' # value for demo
# uncomment the line below to read actual file content
# content = open('files/package_name.spec').read()
open = MagicMock()
open.return_value.__enter__.return_value = StringIO(content)
with open('files/package_name.spec') as f:
    print(f.read())
    f.seek(0)
    f.write('abc')
    f.seek(0)
    print(f.read())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...