在Pytest运行之前执行一些代码

时间:2018-01-05 12:40:36

标签: python pytest

我正在使用Pytest来测试我的代码,我遇到了一些问题,但这是一个令人愤怒的问题。

我的程序的第一件事就是检查是否有可用的设置文件。如果没有,则抛出错误并调用Exception in thread "main" java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader at org.apache.hadoop.hive.ql.session.SessionState.<init>(SessionState.java:394) at org.apache.hadoop.hive.ql.session.SessionState.<init>(SessionState.java:370) at org.apache.hadoop.hive.cli.CliSessionState.<init>(CliSessionState.java:60) at org.apache.hadoop.hive.cli.CliDriver.run(CliDriver.java:708) at org.apache.hadoop.hive.cli.CliDriver.main(CliDriver.java:686) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:543) at org.apache.hadoop.util.RunJar.run(RunJar.java:221) at org.apache.hadoop.util.RunJar.main(RunJar.java:136) 。这在正常运行时很有效,但与Pytest混淆。

我想出的解决方案是通过复制模板设置文件,在测试期间创建一个临时设置文件。我已经编写并成功测试了代码以实现这一目标。

我遇到的问题是我找不到一个真正在其他一切之前发射的Pytest钩子。这导致程序在Pytest尝试创建临时设置文件之前抛出错误。这会导致Pytest在执行任何测试之前失败。

有没有人知道在Pytest执行或加载任何内容之前触发函数的方法?最好是在Pytest本身内。

一些代码上下文:

抛出错误并退出

此代码段在导入exit()模块时运行。

settings

创建临时设置文件

此代码应该是Pytest运行的第一件事。

if len(cycles) == 0:
    log.error("No setting files found. Please create a file " +
              "in the `settings` folder using the Template.py.")
    exit()

删除临时设置文件

这段代码应该是Pytest运行的最后一件事。

def pytest_sessionstart(session):
    """ Create a test settings file """
    folderPath = os.path.dirname(os.path.abspath(__file__))
    folderPath = os.path.split(folderPath)[0] + "/settings"

    srcfile = folderPath + '/Template.py'
    dstfile = folderPath + '/test.py'

    shutil.copy(srcfile, dstfile)

Pytest输出

禁用def pytest_sessionfinish(session, exitstatus): """ Delete the test settings file """ folderPath = os.path.dirname(os.path.abspath(__file__)) folderPath = os.path.split(folderPath)[0] + "/settings" os.remove(folderPath + "/test.py") 的呼叫后,您就可以看到执行顺序。

exit()

5 个答案:

答案 0 :(得分:1)

我设法找到了解决方案。

经过一些额外的研究,我发现Pytest预加载了所有模块。这意味着您永远不能在模块导入之前运行代码,除非您可以在收集阶段之前找到钩子。据我所知,没有这样的钩子。我真的很想在Pytest中做这个工作,但这似乎是不可能的。

相反,我在我的测试文件夹中创建了一个__main__.py文件,其中包含以下内容:

import pytest
import os
import shutil

"""
Create a setting file for the test procedure
"""
folderPath = os.path.dirname(os.path.abspath(__file__))
folderPath = os.path.split(folderPath)[0] + "/settings"

srcfile = folderPath + '/Template.py'
dstfile = folderPath + '/test.py'

shutil.copy(srcfile, dstfile)

"""
Actually run pytest
"""
pytest.main()

"""
Remove the test settings file
"""
os.remove(dstfile)

此代码创建临时设置文件,启动Pytest,然后再次删除临时文件。

我现在可以按如下方式运行测试程序:

$ python3 test

Pytest标志仍然正常工作。例如,如果您想要更详细的输出,可以执行以下操作:

$ python3 test -v

答案 1 :(得分:1)

使用以下代码将conftest.py添加到您的项目中,并将您的代码添加到方法pytest_configure

def pytest_configure(config):
    pass # your code goes here

# other config

答案 2 :(得分:0)

这与您所寻找的不同,但我会质疑您的策略。我怀疑你的代码不是测试友好的。可用的钩子应该足以设置这样的测试前置条件。也就是说,如果您的测试需要一些文件,以某种方式在设置中首先设置该文件,使用拆卸删除它们,或者根据pytest docs使用fixture。

如果您实际上不需要运行测试的文件,除了代码测试它的存在,您可以使用模拟伪造其存在。

但是你在模块导入上运行的代码正在测试(实际上不是测试代码)一个前提条件和用户交互。 而且我认为你不应该这样做。

删除使用import运行的代码的执行(使其成为函数或类)。并在代码的其他初始化位置调用它。

设置文件(使用pytest钩子)。测试使用设置文件的代码。 拆解文件(使用pytest钩子)。

您可以通过使用一些数据结构和函数进行读/写来覆盖使用伪数据读取的数据来完全伪造文件, 模拟文件。

测试测试设置文件存在的代码(使用模拟)。

测试您的其他代码方法。

不要混合东西。尝试编写做一件事的方法。一次测试一件事。测试您的业务代码。用户互动是另一回事。如果您还想测试用户界面,请不要同时进行测试(我的意思是使用相同的代码)。

答案 3 :(得分:0)

首先,pytest尝试使所有导入尽可能地相对:这意味着,如果将测试保留在package/tests中,那么即使conftest也将被导入为package.test.conftest。为此,将调用package.__init__(很遗憾,您是这样)。

为了在运行之前运行某些配置,需要将测试移出软件包树。我最近遇到just such a problem

pytest issue thread所示,pytest_configure实际上是在pytest_sessionstart之前调用的,但是两者都在测试收集之前发生,所以两者都应该没问题。

# contents of tests/conftest
import os
from pathlib import Path
import shutil
import pytest  # if necessary
# cannot import package here (and likely not in `tests.__init__` either)


folderPath = Path.cwd()
folderPath = folderpath / 'package' / 'settings'
srcfile = folderPath / 'Template.py'
dstfile = folderPath / 'test.py'


def pytest_configure(config):
  """ Create a test settings file """
  # can use `config` to use command line options
  shutil.copy(srcfile, dstfile)


def pytest_unconfigure(config):
  """ Delete the test settings file """
  os.remove(dstfile)

如果您的文件夹结构如下:

root_folder
├── package
│   ├── settings
│   │   └── Template.py
│   ├── __init__.py
│   └── module1.py
└── tests
    ├── __init__.py      # Does not import package
    ├── conftest.py      # Does not import package
    └── test_module1.py  # Can import package
然后可以按以下常规方式调用

pytest:从pytest -flags工作时pytest tests -flags(或root_folder)。

如果要根据custom command line flags to pytest以不同的方式设置设置文件,也可以在conftest.py中进行设置。然后可以在pytest_configure中以config.getoption(...)的身份访问它们。


注意:此语法假定Python 3.6+使用Path作为os.removeshutil.copy的参数。

答案 4 :(得分:-2)

要在测试的实际代码之前和之后运行一些代码,可以使用pytest fixtures:

import pytest


@pytest.fixture
def context():
    """What to do before/after the test."""
    print("Entering !")
    yield
    print("Exiting !")


def test_stuff(context):
    """The actual code of your test"""
    print("Running the test")

显示:

Entering !
Running the test
Exiting !

要使用灯具,只需传递一个与测试功能输入同名的参数。 yield指令就像是在测试之前和之后所做的事之间的分隔符。