Git Product home page Git Product logo

pytest-console-scripts's Introduction

pytest-console-scripts

PyPI PyPI - License GitHub Workflow Status codecov

GitHub issues GitHub pull requests GitHub commits since latest release (by date)

Pytest-console-scripts is a pytest plugin for running python scripts from within tests. It's quite similar to subprocess.run(), but it also has an in-process mode, where the scripts are executed by the interpreter that's running pytest (using some amount of sandboxing).

In-process mode significantly reduces the run time of the test suites that run many external scripts. This is speeds up development. In the CI environment subprocess mode can be used to make sure the scripts also work (and behave the same) when run by a fresh interpreter.

Requirements

  • Python 3.8+, or PyPy3,
  • Pytest 4.0 or newer.

Installation

You can install "pytest-console-scripts" via pip from PyPI:

$ pip install pytest-console-scripts

Normally you would add it as a test dependency in tox.ini (see tox documentation).

Usage

This plugin will run scripts that are installed via console_scripts entry point in setup.py, python files in current directory (or anywhere else, if given the path), and Python scripts anywhere else in the path. It will also run executables that are not Python scripts, but only in subprocess mode (there's no benefit in using pytest-console-scripts for this, you should just use subprocess.run).

Here's an example with console_scripts entry point. Imagine we have a python package foo with the following setup.py:

setup(
    name='foo',
    version='0.0.1',
    py_modules=['foo'],
    entry_points={
        'console_scripts': ['foobar=foo:bar']
    },
)

We could use pytest-console-scripts to test the foobar script:

def test_foo_bar(script_runner):
    result = script_runner.run(['foobar', '--version'])
    assert result.returncode == 0
    assert result.stdout == '3.2.1\n'
    assert result.stderr == ''

    script_runner.run('foobar --version', shell=True, check=True)

This would use the script_runner fixture provided by the plugin to run the script and capture its output.

The arguments of script_runner.run are the command name of the script and any command line arguments that should be passed to it. Additionally the following keyword arguments can be used:

  • cwd - set the working directory of the script under test.
  • env - a dictionary with environment variables to use instead of the current environment.
  • stdin - a file-like object that will be piped to standard input of the script.
  • check - raises an exception if returncode != 0, defaults to False.
  • shell - mimic shell execution, this should work well for simple cases, defaults to False.

Type-hinting is also supported. You may type-hint the fixture with the following code:

from pytest_console_scripts import ScriptRunner

def test_foo_bar(script_runner: ScriptRunner) -> None:
    ...

Configuring script execution mode

In the example above the foobar script would run in in-process mode (which is the default). This is fast and good for quick iteration during development. After we're happy with the functionality, it's time to run the script in subprocess mode to simulate real invocation more closely. There are several ways to do this. We can configure it via pytest configuration (for example in tox.ini):

[pytest]
script_launch_mode = subprocess

We can give a command line option to pytest (this will override the configuration file):

$ pytest --script-launch-mode=subprocess test_foobar.py

We can also mark individual tests to run in a specific mode:

@pytest.mark.script_launch_mode('subprocess')
def test_foobar(script_runner):
    ...

Between these three methods the marking of the tests has priority before the command line option that in turn overrides the configuration setting. All three can take three possible values: "inprocess", "subprocess", and "both" (which will cause the test to be run twice: in in-process and in subprocess modes).

Interaction with mocking

It is possible to mock objects and functions inside of console scripts when they are run using pytest-console-scripts but only in inprocess mode. When the script is run in subprocess mode, it is executed by a separate Python interpreter and the test can't mock anything inside of it.

Another limitation of mocking is that with simple Python scripts that are not installed via console_scripts entry point mocking of objects inside of the main script will not work. The reason for that is that when we run myscript.py with $ python myscript.py the script gets imported into __main__ namespace instead of myscript namespace. Our patching of myscript.myfunction will have no effect on what the code in __main__ namespace sees when it's calling myfunction defined in the same file.

See this stackoverflow answer for some ideas of how to get around this.

Suppressing the printing of script run results

When tests involving pytest-console-scripts fail, it tends to be quite useful to see the output of the scripts that were executed in them. We try to be helpful and print it out just before returning the result from script_runner.run(). Normally PyTest captures all the output during a test run and it's not shown to the user unless some tests fail. This is exactly what we want.

However, in some cases it might be useful to disable the output capturing and PyTest provides ways to do it. When capturing is disabled, all test run results will be printed out and this might make it harder to inspect the other output of the tests. To deal with this, pytest-console-scripts has an option to disable the printing of script run results:

$ pytest --hide-run-results test_foobar.py

It's also possible to disable it just for one script run:

result = script_runner.run('foobar', print_result=False)

When printing of script run results is disabled, script output won't be visible even when the test fails. Unfortunately there's no automatic way to print it only if the test fails because by the time a script run completes we don't know whether the test will fail or not. It's possible to do it manually from the test by using:

result.print()

This, combined with --hide-run-results or print_result=False can be used to only print interesting run results when capturing is off.

Package installation and testing during development

Since pytest-console-scripts relies on the scripts being located in the path, it can only run the console scripts from packages that have been installed (if you are interested in working on removing this limitation, take a look at this ticket and in particular this comment). If you want to run the tests quickly during development, the additional installation step would add a significant overhead and slow you down.

There's a way around this: install your package in development mode using pip install -e .. If you use tox, you can take one of its existing virtualenvs (they live in .tox/). Otherwise create a virtualenv just for development, activate it and run python setup.py develop to install your package in development mode. You will need to re-install every time you add a new console script, but otherwise all the changes to your code will be immediately picked up by the tests.

Contributing

Contributions are very welcome. Tests can be run with tox, please ensure the coverage at least stays the same before you submit a pull request.

License

Distributed under the terms of the MIT license, "pytest-console-scripts" is free and open source software.

Issues

If you encounter any problems, please file an issue along with a detailed description.


Pytest-console-scripts was initially generated with Cookiecutter along with @hackebrot's Cookiecutter-pytest-plugin template.

pytest-console-scripts's People

Contributors

a96tudor avatar aluisioasg avatar ansasaki avatar ederag avatar hexdecimal avatar jonchang avatar kianmeng avatar kvas-it avatar nicoddemus avatar nymanrobin avatar s-t-e-v-e-n-k avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

pytest-console-scripts's Issues

Test fails when called alone, succeeds with whole test module

I have an argparse-based script named foo that accepts multiple flags (say --a and --b).

I wrote a test module for this script, testing each flag separately,

def test_foo_a(script_runner):

    result = script_runner.run(
        "foo",
        "--a",
    )

    assert result.success
    assert result.stderr == ""

def test_foo_b(script_runner):

    result = script_runner.run(
        "foo",
        "--b",
    )

    assert result.success
    assert result.stderr == ""

What happens is that,

pytest -k "test_foo_b"

fails (and I know why, I can see the traceback), but,

pytest path/to/test_module.py

succeeds.

I can try to replicate this with a script simpler than what I have, but perhaps in the meantime you can find the problem if it's a bug.

Logging doesn't get properly initialized in inprocess mode

If the script under test uses Python logging and initializes it, for example with logging.basicConfig(), the logging subsystem will not behave as expected when run with pytest-console-scripts in inprocess mode. In order for this to work properly we would need to save the configuration of the logging subsystem, reset it to some initial state, wherein it can be initialized with logging.basicConfig() and then revert everything back after the script execution finishes.

Can I mock a function in the called script?

Im having trouble mocking a function in the script im calling. Is it possible?

file: foo.py

def func(x):
    raise Exception

if __name__ == "__main__":
    func('works!')

file: test.py

import foo

def test(script_runner, monkeypatch):
    monkeypatch.setattr(foo, "func", lambda x: print(x))

    # this does print
    foo.func("mocked function!")

    ret = script_runner.run("foo.py")

    # however, this does not work, an exception is raised
    assert ret.success

Thank you.

Improve command path search

On my current system (CentOS), console scripts are not installed in the same folder as Python. (They are installed in /usr/local/bin rather than /usr/bin.)

To fix this, I am wondering if a fix like below at line 108 could work, or if there is a design consideration I am missing:
script = py.path.local(distutils.spawn.find_executable(command))

It looks like this would also resolve Issue #17.

Mocking script methods when testing with pytest-console-scripts

Hi,

i've started using your package to test some legacy code I need to refactor.

Took me a while to figure out an essential part: how to mock functions called by the script.
I think you should write a comment on this part in the Readme, as this appear to be a common use case for your PCS.

So far, tell me if I'm wrong, it's possible to mock function when the script is called as
"inprocess". If it's called as subprocess, it's not possible to mock, afaics.

The problem I'm facing right now is that I can't find a way to mock calls to functions internal to the script. Maybe it's just not possible in python.

FYI https://stackoverflow.com/questions/66676962/how-to-mock-a-function-within-a-python-script-tested-with-pytest-console-scripts

Mocking libraries

Hi,

I have a question - how can I mock something that I test via script_runner?
I know it's meant for integration tests, but sometimes it's useful...

I mean my script does an import that I'd like to mock, for example, import requests

Cheers!

Please package tests and the LICENSE file

Hi,

the pypi package doesn't include the LICENSE file and tests. Could you please add them and also add a test target to setup.sy so you can run the tests correctly.

python setup.py test

This is required to package it for distributions like openSUSE and Fedora.

Thanks!

Running as 'inprocess' when using the decorator with 'subprocess' after upgrade to pytest 4.1.0

When trying to run a test as 'subprocess' flagged in the decorator (@pytest.mark.script_launch_mode('subprocess')), the test actually runs as 'inprocess'. This same test worked fine with pytest-4.0.2, but looks got broken after upgrading to pytest-4.1.0. In both cases console-scripts was 0.1.7 and Python 3.4.5.

The workaround for me was to use the pytest command line option --script-launch-mode=subprocess

1.3.1: missing install-time dependencies

During pep440 test suite execution I found that pytest failed because missing pkg_resources module in build env.

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-pep440-0.1.2-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-pep440-0.1.2-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra
Traceback (most recent call last):
  File "/usr/bin/pytest", line 8, in <module>
    sys.exit(console_main())
  File "/usr/lib/python3.8/site-packages/_pytest/config/__init__.py", line 187, in console_main
    code = main()
  File "/usr/lib/python3.8/site-packages/_pytest/config/__init__.py", line 145, in main
    config = _prepareconfig(args, plugins)
  File "/usr/lib/python3.8/site-packages/_pytest/config/__init__.py", line 324, in _prepareconfig
    config = pluginmanager.hook.pytest_cmdline_parse(
  File "/usr/lib/python3.8/site-packages/pluggy/_hooks.py", line 265, in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
  File "/usr/lib/python3.8/site-packages/pluggy/_manager.py", line 80, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
  File "/usr/lib/python3.8/site-packages/pluggy/_callers.py", line 55, in _multicall
    gen.send(outcome)
  File "/usr/lib/python3.8/site-packages/_pytest/helpconfig.py", line 102, in pytest_cmdline_parse
    config: Config = outcome.get_result()
  File "/usr/lib/python3.8/site-packages/pluggy/_result.py", line 60, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/usr/lib/python3.8/site-packages/pluggy/_callers.py", line 39, in _multicall
    res = hook_impl.function(*args)
  File "/usr/lib/python3.8/site-packages/_pytest/config/__init__.py", line 1017, in pytest_cmdline_parse
    self.parse(args)
  File "/usr/lib/python3.8/site-packages/_pytest/config/__init__.py", line 1305, in parse
    self._preparse(args, addopts=addopts)
  File "/usr/lib/python3.8/site-packages/_pytest/config/__init__.py", line 1188, in _preparse
    self.pluginmanager.load_setuptools_entrypoints("pytest11")
  File "/usr/lib/python3.8/site-packages/pluggy/_manager.py", line 287, in load_setuptools_entrypoints
    plugin = ep.load()
  File "/usr/lib64/python3.8/importlib/metadata.py", line 77, in load
    module = import_module(match.group('module'))
  File "/usr/lib64/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "/usr/lib/python3.8/site-packages/_pytest/assertion/rewrite.py", line 168, in exec_module
    exec(co, module.__dict__)
  File "/usr/lib/python3.8/site-packages/pytest_console_scripts.py", line 6, in <module>
    import pkg_resources
ModuleNotFoundError: No module named 'pkg_resources'

And indeed in


is import of that module and in
install_requires=['pytest>=4.0.0'],

is missing that install time dependency.

Quirck solution could be patch like below

--- a/setup.py
+++ b/setup.py
@@ -21,7 +21,7 @@
     long_description=read('README.md'),
     long_description_content_type='text/markdown',
     py_modules=['pytest_console_scripts'],
-    install_requires=['pytest>=4.0.0'],
+    install_requires=['pytest>=4.0.0', 'pkg_resources'],
     python_requires='>=3.6',
     setup_requires=['setuptools-scm'],
     classifiers=[

however basing on pypa/setuptools#3279 better would be migrate to use importlib_metadata like https://git.smhi.se/climix/climix/-/merge_requests/165.patch

Parity with subprocess.run

The readme explains that the console_scripts fixture works similarly to subprocess.run. I'd like to make it even more similar so that it's easier to port tests using tools similar to subprocess.run and also have it be more obvious how the fixture works.

Specifically I'd like to suggest the following changes:

  • Have the first parameter to script_runner.run be a sequence similar to how subprocess.run works and support giving PathLike objects with arguments. Something like: Sequence[str | os.PathLike[str]]. Possibly deprecate *arguments.
  • Deprecate **options and add support for keywords by hand. This is to make it more clear when something isn't actually supported and helps with controlling the input for what is supported. **options breaks type-checking and won't warn on bad parameters.
  • Support the check keyword. An exception will be raised for "inprocess" mode on non-zero returns after the debug results are printed. Possibly raising subprocess.CalledProcessError with the appropriate values to mimic subprocess.run.
  • shell=True for "inprocess" can probably be supported with subprocess.list2cmdline and shlex.split.

script_runner should keep the defaults of everything being in Unicode. I don't want to complicate it with bytes handling.

def test_subprocess_like_args(script_runner):
    # Examples assuming "inprocess" mode.
    result = script_runner.run(['foobar', '--version'], check=True)  # check=True raises if result.returncode is non-zero
    test_script: Path = ...  # Direct path to script.py
    script_runner.run([test_script], check=True)  # Pass a PathLike as an argument
    script_runner.run(test_script, check=True)  # str or PathLike also works as a single direct argument

    script_runner.run("script --test", shell=True, check=True)  # Could be supported in theory.

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xcb in position 24: invalid continuation byte

The stderr from fqtools seems to be a problem:

ret = script_runner.run('fqtools', '-h')
assert ret.stdout == 'OK'

Generates:

.# Running console script: fqtools -h
# Script return code: 1
# Script stdout:

# Script stderr:
Traceback (most recent call last):
  File "/ebio/abt3_projects/software/dev/miniconda3_dev/envs/MGSIM/lib/python3.6/site-packages/py/_path/common.py", line 171, in read
    return f.read()
  File "/ebio/abt3_projects/software/dev/miniconda3_dev/envs/MGSIM/lib/python3.6/codecs.py", line 321, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xcb in position 24: invalid continuation byte

Running fqtools -h outside of pytest works just fine on that fastq file.

I'm using pytest-console-scripts 0.2.0 and fqtools 2.0 on Ubuntu 18.04.4

It seems pretty easy to generate a UnicodeDecodeError: 'utf-8' error with script_runner.run(). For instance, just running bash commands such as script_runner.run('pwd') will cause the same error.

Running scripts that have not been installed

I can succesfully test my console scripts when running from a Tox environment. However, I would also like to run these tests outside when not using Tox. The readme is not clear about whether this is possible or not.

My project layout is as follows:

src/
  package_name/
    __main__.py
    ...
setup.py

My setup.py:

setup(
    ...
    entry_points={
        'console_scripts': [
            'mycmd = package_name.__main__:main',
        ],
    ...
)

When running pytests in a development environment, it is common to set PYTHONPATH=src. Is it possible to point pytest-console-scripts to setup.py somehow in this case so that it can find the console scripts?

script_runner fixture type-hint.

I always type-hint my pytest fixtures. I hoped that I could import ScriptRunner from its module but type-hinting is not setup there. I can add hinting as a PR but it'd require converting the single module into a package to make the hints importable.

Until then I'll just create protocols, and if anyone else wants to type-hint their fixtures then you can borrow mine:

class RunResult(Protocol):
    """Result of running a script."""

    success: bool
    returncode: int
    stdout: str
    stderr: str


class ScriptRunner(Protocol):
    """Protocol for pytest-console-scripts ScriptRunner."""

    def run(self, command: str, *arguments: str, **options: Any) -> RunResult:
        ...


def test_with_hint(script_runner: ScriptRunner) -> None:
    ...

Warnings when running tests

Environment

python 3.6
pytest 3.9.1
virtualenv 16.0.0

Observed behaviour

When running the tests, they all pass, but there are a series of warnings that are caused by Pytest depreciations:

/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
  return getattr(object, name, default)

/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
  return getattr(object, name, default)

/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
  return getattr(object, name, default)

/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
  return getattr(object, name, default)

/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
  return getattr(object, name, default)

/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
  return getattr(object, name, default)

/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
  return getattr(object, name, default)

/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
  return getattr(object, name, default)

/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
  return getattr(object, name, default)

/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
  return getattr(object, name, default)

/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
  return getattr(object, name, default)

/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
  return getattr(object, name, default)
/Users/tma33/Documents/open-source/pytest-console-scripts/.tox/py36/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
  return getattr(object, name, default)

-- Docs: https://docs.pytest.org/en/latest/warnings.html

Expected behaviour

No warnings are shown

Additional comments

From some further investigation, it looks like the warnings are caused by pytest's latest release (3.9.1). If the tests are run using pytest 3.8.2 this warnings are no longer raised.

set PYTHONHOME instead of PYTHONPATH in test_run_scripts.py

Currently, on Ubuntu (and probably also Debian), everything in test_run_scripts.py bombs with errors similar to the following:

error: can't create or remove files in install directory

The following error occurred while trying to add or remove files in the
installation directory:

[Errno 2] No such file or directory: '/tmp/pytest-of-kelledin/pytest-1/venv0/local/lib/python2.7/dist-packages/test-easy-install-14689.write-test'

The installation directory you specified (via --install-dir, --prefix, or
the distutils default setting) was:

/tmp/pytest-of-kelledin/pytest-1/venv0/local/lib/python2.7/dist-packages/

This directory does not currently exist. Please create it and try again, or
choose a different installation directory (using the -d or --install-dir
option).

I managed to get past this problem on my system by removing the env['PYTHONPATH'] setting, and instead setting env['PYTHONHOME'] = self.path.strpath. This appears to more closely mimic the actions of a virtualenv "activate" script fragment, so I suspect it's the proper thing to do for now. (To be fair, it would be nice if there was something like a virtualenv.activate_environment() method, so we didn't have to guess as to how to activate a virtualenv from within Python code.)

The fact that Debian-based distros use dist-packages (and apparently hardcode this into setup.py develop usage) probably also had something to do with triggering this error.

Setup coverage report in tests

Currently, the Contributing section from the README requires that contributions made the repository to keep the same level of test coverage. There is no coverage report setup in tox.ini, though.

In consequence, I suggest we should add this report when running tests.

Testing scripts which trigger user input

Is there a way to mock an stdin from command line?

say I have a script with an entry point defined which behaves like this:

  • if I call my-cli-tool with no arguments it triggers the user to inject some info (say a,b,c)
  • if I call it as my-cli-tool --a some_thing --b some_other_thing --c a_different_thing it just works

I have tried naïvely to test this as

def test_my_cli_tool(script_runner):
    result = script_runner.run(
        "my-cli-tool",
        "--a 'some_thing'",
        "--b 'some_other_thing'",
        "--c 'a_different_thing'",
    )

    assert result.success
    assert result.stderr == ""

but it fails because it expects an input from the user even if I already set the flag.

It's like it is running first my-cli-tool with no arguments first.

extremely slow execution of script tests

Hey,

I am using the pytest-console-scripts in order to test if some of my scripts are working fine. The scripts perform a single training step for a machine learning model. Until recently the script test was working well, but since I created a new environment and reinstalled pytest-console-scripts, the script test is about ten times slower. I have no idea what went wrong... Do you know what could be going on here?

Best,
Stefaan

Strange behaviour with Pytest when running script is imported as module in another test file.

Hi, everyone!
Let's assume I have a project with the following structue:

.
├── mock_check
│   ├── __init__.py
│   └── my_script.py
└── test_my_script.py

Files contents are:

# mock_check/my_script.py
import requests
import sys

def some_func():
    return 1

print(sys.argv)
print(requests.get("http://fake.url").json())

# test_my_script.py
import pytest


@pytest.fixture
def mock_requests_get(mocker):
    mock = mocker.patch("requests.get")
    mock.return_value.json.return_value = "test_output"
    return mock


@pytest.mark.script_launch_mode('inprocess')
def test_mock_check(mock_requests_get, script_runner):
    ret = script_runner.run("mock_check/my_script.py", print_result=True, shell=True)
    assert ret.success
    assert ret.stdout == "['mock_check/my_script.py']\ntest_output\n"
    assert ret.stderr == ''

When I run pytest this single test passes.

Suppose I want to test some_func() separately.
So I make a new file:

# test_import_my_script.py
from mock_check.my_script import some_func

def test_some_func():
    assert some_func() == 1

And project structuer becames this:

.
├── mock_check
│   ├── __init__.py
│   └── my_script.py
├── test_import_my_script.py
└── test_mock_check.py

Now when I run pytest it gathers both files and command terminates with error:

ERROR test_import_my_script.py - requests.exceptions.ConnectionError: HTTPConnectionPool(host='fake.url', port=80): Max retries exceeded...

which means that mock stopped working.
Aslo the output of print(sys.argv) instead of ['mock_check/my_script.py'] now looks like this:

['/home/weiss-d/.cache/pypoetry/virtualenvs/mocker-m3ZuwkOU-py3.9/bin/pytest']

But when I run just pytest test_my_script.py it again passes.

This behaviour looks very strange to me, considering that every test per function should be executed isolated by Pytest, not to say about different test files.
Is it a bug, or I am missing something?

Fedora package for pytest-console-scripts

I could not find a Fedora package for pytest-console-scripts.
Can you package it for Fedora? A rpm spec file can be easily obtained using pyp2rpm which creates a spec file from the pip package.

If you are not interested, I can package it in Fedora, with your permission.

depends on #8

Warnings caused by pytest4

With the update to pytest 4, some changes cause warnings to be risen when using pytest_console_scripts. Here is a dump of the warning:

py27/lib/python2.7/site-packages/pytest_console_scripts.py:60: RemovedInPytest4Warning: MarkInfo objects are deprecated as they contain merged marks which are hard to deal with correctly.
Please use node.get_closest_marker(name) or node.iter_markers(name).
Docs: https://docs.pytest.org/en/latest/mark.html#updating-code
  mark_mode = launch_mode_mark.args[0] if launch_mode_mark else None

This can be solved by following the instructions available here: https://docs.pytest.org/en/latest/mark.html#updating-code

“python_requires” should be set with “>=3.6”, as pytest-console-scripts 1.2.2 is not compatible with all Python versions.

Currently, the keyword argument python_requires of setup() is not set, and thus it is assumed that this distribution is compatible with all Python versions.
However, I found it is not compatible with Python 2. My local Python version is 2.7, and I encounter the following error when executing “pip install pytest-console-scripts”

Collecting pytest-console-scripts
  Downloading pytest-console-scripts-1.2.2.tar.gz (14 kB)
    ERROR: Command errored out with exit status 1:
     command: /usr/local/bin/python -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-rXjTtr/pytest-console-scripts/setup.py'"'"'; __file__='"'"'/tmp/pip-install-rXjTtr/pytest-console-scripts/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-install-rXjTtr/pytest-console-scripts/pip-egg-info
         cwd: /tmp/pip-install-rXjTtr/pytest-console-scripts/
    Complete output (7 lines):
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-install-rXjTtr/pytest-console-scripts/setup.py", line 21, in <module>
        long_description=read('README.md'),
      File "/tmp/pip-install-rXjTtr/pytest-console-scripts/setup.py", line 7, in read
        with open(file_path, encoding='utf-8') as f:
    TypeError: 'encoding' is an invalid keyword argument for this function
    ----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

I found that setup.py used a Python3-specific keyword argument encoding for the function open, which lead to installation failure of Inject in Python 2. I also noticed that the project is dropping support for Python 3.5, so I think it better to set python_requires with ">=3.6" rather than ">=3".

Way to fix:
modify setup() in setup.py, add python_requires keyword argument:

setup(…
     python_requires=">=3.6"
     …)

Thanks for your attention.
Best regrads,
PyVCEchecker

PyPI Wheels?

Thanks for pytest-console-scripts!

Would it be possible to get PyPI wheels distributed along with the sdists? As a downstream user, this is nice as it reduces the amount of install-time code that gets executed (e.g. setup.py, build systems, etc.)

I've generally taken to generating and hashing the .tar.gz and .whl packages in CI and archiving them, then actually uploading them offline, as this does a good job of ensuring no unexpected files/line endings/etc. get introduced, while avoiding the potential damage from credential issues online.

Add option to suppress printing RunResult on init

Hello, I would like the option to suppress the automatic printing of the RunResult class in the init (Lines 107-109 in pytest_console_scripts.py).

I think that a relatively easy way to do this is to have RunResult take a default parameter - print_output = True and use that as a flag to print the return code, stdout, and stderr.

This flag could be set in the ScriptRunner object and then passed through the run commands.

Thanks,

thank you for writing pytest-console-scripts

I'm stuck with testing a click app with CliRunner in a case where a script executes when run from console, but fails as pytest with ambiguous message, so I was looking for a pytest plugin desperately.

Very intuitive interface of pytest-console-scripts and docs addressing the user needs, result.print() should be a savior.

Thank you for putting this valuable project together.

Add support for passing environment variables to the script under test

It's already possible to do it via simply modifying os.environ before running the script and then restoring it but it would be nice if script_runner.run(...) had a parameter for that. Actually run_subprocess already does: env argument will be just passed to subprocess.Popen so we just need to implement it for run_inprocess.

mocked stdio are not like the real thing

All of the sys.std* are of type io.TextIOWrapper which has a .buffer of type io.BufferedReader.

Without the mock implementing TextIOWrapper, it wont work with any the code being tested that needs to access those buffers , or any of the other features of the real stdii handles which are not implemented by StringIO, and also StringIO exposes methods which are not valid on the real stdio handles.

Another important feature of the real stdio handles is the new reconfigure in py37+, which is useful to force a sensible encoding on a stdio stream.

console script subprocess calls are skipped

I am trying to test a console script that itself runs calls subprocess a few times in the script. These subprocess calls are just skipped when using pytest-console-scripts-0.2.0. The rest of the console script, but it simply bypasses the subprocess calls. I don't see anything about this in the pytest-console-scripts docs.

Cross OS support?

Hi, I've tried using this package for testing my command line scripts and it works great in linux and OSX. So far, I have not gotten it to work in Windows. I think the error is coming from how I'm passing in the stdin argument. I have little experience with Windows and so I was wondering if it is simply not supported before I spend too much more time trying to get this to work.

Thanks!

Decode error when using inprocess tests on windows 10

What's still unclear to me, is how to convert your library that is using entry_points={"console_scripts":[]} in setup.py from working with only subprocess to working with inprocess... For me, testing on Windows, I only get the following error during inprocess execution:

Traceback (most recent call last):
  File "C:\Users\myuser\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pytest_console_scripts.py", line 195, in exec_script
    compiled = compile(script.read(), str(script), 'exec', flags=0)
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\codecs.py", line 322, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x90 in position 2: invalid start byte

Perhaps this is a Windows-specific issue with decoding stdout and stderr?

Originally posted by @arcsector in #57 (comment)

Any help on this would be appreciated, as I know that since subprocess takes care of this automagically in the backend, this makes sense that it's only an inprocess issue.

`pytest-console-scripts` causes `pytest-xdist` tests to fail

I tried to run tests for pytest-xdist 3.4.0 with pytest-console-scripts 1.4.1 installed and disabled using -p no:console-scripts set in the PYTEST_ADDOPTS environment variable. And, surprisingly, pytest-xdist tests fails:

============================= test session starts ==============================
platform sunos5 -- Python 3.9.16, pytest-7.4.3, pluggy-1.3.0 -- $(BUILD_DIR)/.tox/py39/bin/python
cachedir: .tox/py39/.pytest_cache
rootdir: $(BUILD_DIR)
configfile: tox.ini
testpaths: testing
plugins: xdist-3.4.0
collecting ... collected 203 items

testing/acceptance_test.py::TestDistribution::test_n1_pass PASSED        [  0%]
testing/acceptance_test.py::TestDistribution::test_n1_fail FAILED        [  0%]

=================================== FAILURES ===================================
________________________ TestDistribution.test_n1_fail _________________________

self = <acceptance_test.TestDistribution object at 0x7fffadcb2760>
pytester = <Pytester PosixPath('/tmp/pytest-of-marcel/pytest-79/test_n1_fail0')>

    def test_n1_fail(self, pytester: pytest.Pytester) -> None:
        p1 = pytester.makepyfile(
            """
            def test_fail():
                assert 0
        """
        )
        result = pytester.runpytest(p1, "-n1")
>       assert result.ret == 1
E       assert <ExitCode.USAGE_ERROR: 4> == 1
E        +  where <ExitCode.USAGE_ERROR: 4> = <RunResult ret=ExitCode.USAGE_ERROR len(stdout.lines)=0 len(stderr.lines)=5 duration=0.03s>.ret

$(BUILD_DIR)/testing/acceptance_test.py:32: AssertionError
----------------------------- Captured stderr call -----------------------------
ERROR: usage: __main__.py [options] [file_or_dir] [file_or_dir] [...]
__main__.py: error: unrecognized arguments: -n1
  inifile: None
  rootdir: /tmp/pytest-of-marcel/pytest-79/test_n1_fail0

=========================== short test summary info ============================
FAILED testing/acceptance_test.py::TestDistribution::test_n1_fail - assert <E...
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!

With pytest-console-scripts uninstalled all pytest-xdist tests pass.

print_result='on_fail'

Unfortunately there's no easy way to print it only if the test fails because by the time a script run completes we don't yet know whether the test will fail or not

How about adding the option: print_result='on_fail', and in this case, the output is saved in memory until it is known whether the test has failed or not? If the test fails, then the output is printed, but otherwise it is discarded from memory.

Unable to run script within module from tests

I currently have this module where I need to execute the script ouroboros/ouroboros in a test using inprocess.

I'm running like so:

def test_main_with_latest(mocker, create_container, script_runner):
    #mocker.patch('sys.argv', [''])
    mocker.patch.dict('os.environ',
                      {'INTERVAL': '5',
                       'LOGLEVEL': 'debug',
                       'RUNONCE': 'true',
                       'CLEANUP': 'true',
                       'MONITOR': test_container_name})
    status = script_runner.run('ouroboros/ouroboros', '--version', cwd='../../')
    #assert status.success
    assert status.stderr == '3.2.1\n'

But looks like every time, this path gets prepended:

FileNotFoundError: [Errno 2] No such file or directory: '/usr/local/opt/python/bin/ouroboros/ouroboros'

How do I go about running the script using relative paths within my tests?

Thanks

Allow overriding `universal_newlines` when running a subprocess

Could you possibly modify run_subprocess() to something like this?

    def run_subprocess(self, command, *arguments, **options):
        stdin = ''
        if 'stdin' in options:
            stdin = options['stdin'].read()
            options['stdin'] = subprocess.PIPE
        if 'universal_newlines' not in options:
            options['universal_newlines'] = True
        p = subprocess.Popen([command] + list(arguments),
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                             **options)
        stdout, stderr = p.communicate(stdin)
return RunResult(p.returncode, stdout, stderr)

It would be really convenient to be able to override this option.

Encoding issues under Windows

I'm running into a crash related to encoding on Windows:

(.venv) C:\Users\Brecht\My Documents\Opqode\rinohtype>pytest tests_regression -k test_version[inprocess]
============================= test session starts =============================
platform win32 -- Python 3.8.6, pytest-5.4.3, py-1.8.2, pluggy-0.13.1
rootdir: C:\Users\Brecht\My Documents\Opqode\rinohtype, inifile: setup.cfg
plugins: assume-2.2.1, console-scripts-0.2.0
collected 41 items / 40 deselected / 1 selected

..\..\..\My Documents\Opqode\rinohtype\test_rinoh.py F                   [100%]

================================== FAILURES ===================================
___________________________ test_version[inprocess] ___________________________

script_runner = <ScriptRunner inprocess>

    def test_version(script_runner):
        ret = script_runner.run('rinoh', '--version')
>       assert ret.success
E       assert False
E        +  where False = <pytest_console_scripts.RunResult object at 0x000001F1725F7520>.success

C:\Users\Brecht\Documents\Opqode\rinohtype\tests_regression\test_rinoh.py:21: AssertionError
---------------------------- Captured stdout call -----------------------------
# Running console script: rinoh --version
# Script return code: 1
# Script stdout:

# Script stderr:
Traceback (most recent call last):
  File "c:\users\brecht\my documents\opqode\rinohtype\.venv\lib\site-packages\py\_path\common.py", line 177, in read
    return f.read()
  File "C:\Python38\lib\encodings\cp1252.py", line 23, in decode
    return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x90 in position 2: character maps to <undefined>

=========================== short test summary info ===========================
FAILED ..\..\..\My Documents\Opqode\rinohtype\test_rinoh.py::test_version[inprocess]
====================== 1 failed, 40 deselected in 1.73s =======================

Running rinoh --version in the same terminal runs without issues.

I can't seem to figure out how to get a full traceback pointing out the relevant piece of pytest-console-scripts code.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.