我正在使用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)
禁用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()
答案 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.remove
和shutil.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指令就像是在测试之前和之后所做的事之间的分隔符。