我有一个夹具,它返回如下值:
I have a fixture that returns a value like this:
import pytest
@pytest.yield_fixture(scope="module")
def oneTimeSetUp(browser):
print("Running one time setUp")
if browser == 'firefox':
driver = webdriver.Firefox()
print("Running tests on FF")
else:
driver = webdriver.Chrome()
print("Running tests on chrome")
yield driver
print("Running one time tearDown")
这个夹具从另一个正在读取命令行选项的夹具获取浏览器值.
This fixture gets the browser value from another fixture which is reading the command line option.
然后我有一个测试类,其中我有多个测试方法,它们都希望使用相同的返回值驱动程序来继续测试.
Then I have a test class where I have more than one test methods and they all want to consume the same returned value driver to proceed the tests.
import pytest
@pytest.mark.usefixtures("oneTimeSetUp")
class TestClassDemo():
def test_methodA(self):
# I would like to use the driver value here
# How could I do this?
# Something like this
self.driver.get("https://www.google.com")
self.driver.find_element(By.ID, "some id")
print("Running method A")
def test_methodB(self):
print("Running method B")
使用 self.driver 失败并显示错误消息
Using self.driver fails with the error message
self = <test_class_demo.TestClassDemo object at 0x102fb6c18>
def test_methodA(self):
> self.driver.get("https://www.google.com")
E AttributeError: 'TestClassDemo' object has no attribute 'driver'
我知道我可以将夹具作为参数传递给我想使用它的每个方法,但这不是最好的方法,因为我在每个方法中都需要它并且应该可以将它传递给类然后在所有的测试方法中使用它.
I am aware that I can pass the fixture as an argument to every method where I want to use that, but that is not the best way because I need this in every method and it should be possible to pass it to the class and then use it in all the test methods.
使驱动程序对象可用于方法的最佳方法是什么?
What is the best way that I can make the driver object available to the methods?
编辑 1:
按照建议在 conftest.py 中创建夹具
Created the fixture in conftest.py like this as suggested
@pytest.yield_fixture(scope="class") # <-- note class scope
def oneTimeSetUp(request, browser): # <-- note the additional `request` param
print("Running one time setUp")
if browser == 'firefox':
driver = webdriver.Firefox()
driver.maximize_window()
driver.implicitly_wait(3)
print("Running tests on FF")
else:
driver = webdriver.Chrome()
print("Running tests on chrome")
## add `driver` attribute to the class under test -->
if request.cls is not None:
request.cls.driver = driver
## <--
yield driver
print("Running one time tearDown")
我还有一个类,TestClassDemo 中需要哪个对象,我需要将相同的驱动程序实例传递给该类.将其视为 ABC 类
I have one more class, which object in need in the TestClassDemo and I need to pass the same driver instance to the class. Consider it as class ABC
class ABC():
def __init(self, driver):
self.driver = driver
def enterName(self):
# Do something with driver instance
然后在TestClassDemo中
Then in the TestClassDemo
@pytest.mark.usefixtures("oneTimeSetUp", "setUp")
class TestClassDemo(unittest.TestCase):
# I need to create an object of class ABC, so that I can use it here
# abc = ABC(self.driver)
@pytest.fixture(scope="class", autouse=True)
def setup(self):
self.abc = ABC(self.driver)
# I tried this, but it's not working
# This error message shows up
# AttributeError: 'TestClassDemo' object has no attribute 'driver'
def setup_module(self):
self.abc = ABC(self.driver)
# This also does not work
# Error message -> AttributeError: 'TestClassDemo' object has no attribute 'abc'
def test_methodA(self):
self.driver.get("https://google.com")
self.abc.enterName("test")
print("Running method A")
def test_methodB(self):
self.abc.enterName("test")
print("Running method B")
这个 abc 对象应该也可以在其他 test_ 方法中使用.
This abc object should be usable in other test_ methods also.
所有这些类都在单独的模块中,我的意思是在单独的 .py 文件中.
另外请在答案中解释什么是最好的使用方法而不是屈服驱动程序实例.
Also please explain in the answer what is the best way to use instead of yield driver instance.
编辑 2:
对于这个没有 yield 的示例,运行 oneTimeTearDown 的最佳方法是什么?我在 yield 之后运行了 tearDown 步骤
For this example without yield, what would be the best way to run oneTimeTearDown also? I was running the tearDown steps after the yield
@pytest.fixture(scope="class")
def oneTimeSetUp(request, browser):
print("Running one time setUp")
if browser == 'firefox':
driver = webdriver.Firefox()
driver.maximize_window()
driver.implicitly_wait(3)
print("Running tests on FF")
else:
driver = webdriver.Chrome()
print("Running tests on chrome")
if request.cls is not None:
request.cls.driver = driver
我也尝试使用 UnitTest 类,但是当我使用 def setUpClass(cls) 时,我无法使用在 test_ 方法中实例化的对象.所以我无法弄清楚如何实现这一目标.
Also I tried using UnitTest class, but when I use def setUpClass(cls), I was not able to use the objects instantiated in the test_ methods. So I couldn't not figure out how to achieve that.
我还想从命令行提供命令行参数,例如浏览器,当我尝试 unittest 时,我必须在每个类中编写命令行参数.我只想在一个地方提供它们,比如一个测试套件.所以conftest在这里帮助了我.
I also wanted to provide command line arguments like browser from the command line and when I tried unittest, I had to write the command line argument in every class. I wanted to provide them in one place only, like a test suite. So conftest helped me here.
我有一个关于 stackoverflow 的问题,但没有得到回复.你也可以看看吗?Python unittest 将参数传递给父测试类
I had a question on stackoverflow but didn't get a response. Could you please take a look at that also? Python unittest passing arguments to parent test class
谢谢
谢谢
py.可能对您有帮助的文本 unittest 集成文档 ...使用内置的 request
固定装置.否则,我不知道在不提供命名夹具作为方法参数的情况下访问夹具返回值的方法.
There's a technique outlined in the py.text unittest integration documentation that may be helpful to you ... using the built-in request
fixture. Otherwise, I'm not aware of way to access the return value of a fixture without providing the named fixture as a method param.
@pytest.yield_fixture(scope="class") # <-- note class scope
def oneTimeSetUp(request, browser): # <-- note the additional `request` param
print("Running one time setUp")
if browser == 'firefox':
driver = webdriver.Firefox()
print("Running tests on FF")
else:
driver = webdriver.Chrome()
print("Running tests on chrome")
## add `driver` attribute to the class under test -->
if request.cls is not None:
request.cls.driver = driver
## <--
yield driver
print("Running one time tearDown")
现在您可以在 TestClassDemo
中将 driver
作为类属性访问,就像您在示例中所做的那样(即 self.driver
应该可以工作).
Now you can access the driver
as a class attribute in TestClassDemo
, as you have in your example (i.e. self.driver
should work).
需要注意的是,您的夹具必须使用 scope='class'
,否则 request
对象将不具有 cls
属性.
The caveat is that your fixture must use scope='class'
, otherwise the request
object will not possess a cls
attribute.
希望对你有帮助!
更新
我还有一个类,TestClassDemo 中需要哪个对象,我需要将相同的驱动程序实例传递给该类.将其视为 ABC 类
I have one more class, which object in need in the TestClassDemo and I need to pass the same driver instance to the class. Consider it as class ABC
如果没有更多上下文就很难知道,但在我看来,您可能可以在实例化 driver
的同时实例化 ABC
对象...在 oneTimeSetUp
夹具中.比如……
It's difficult to know without more context, but it seems to me that you can probably get away with instantiating an ABC
object at the same time that you instantiate the driver
... in the oneTimeSetUp
fixture. For example ...
@pytest.yield_fixture(scope="class")
def oneTimeSetUp(request, browser):
print("Running one time setUp")
if browser == 'firefox':
driver = webdriver.Firefox()
driver.maximize_window()
driver.implicitly_wait(3)
print("Running tests on FF")
else:
driver = webdriver.Chrome()
print("Running tests on chrome")
if request.cls is not None:
request.cls.driver = driver
request.cls.abc = ABC(driver) # <-- here
yield driver
print("Running one time tearDown")
但如果你只需要一个或两个测试类的 ABC 实例,下面是你可以如何在类定义中使用固定装置...
But if you only need the ABC instance for a test class or two, here's how you might use a fixture inside the class definition ...
@pytest.mark.usefixtures("oneTimeSetUp", "setUp")
class TestClassDemo(unittest.TestCase):
@pytest.fixture(autouse=True)
def build_abc(self, oneTimeSetUp): # <-- note the oneTimeSetup reference here
self.abc = ABC(self.driver)
def test_methodA(self):
self.driver.get("https://google.com")
self.abc.enterName("test")
print("Running method A")
def test_methodB(self):
self.abc.enterName("test")
print("Running method B")
我不会对第二个例子特别满意.第三种选择是使用另一个 yield_fixture 或类似的,它与 oneTimeSetUp
完全分离,并返回一个已包装驱动程序的 ABC 实例.
I wouldn't be particularly happy with the second example. A third option would be to have another yield_fixture, or similar, that is completely separate from oneTimeSetUp
and returns an ABC instance with the driver already wrapped.
哪种方式最适合您?不确定.您需要根据自己的工作来决定.
Which way is best for you? Not sure. You'll need to decide based on what you're working with.
为后代注意 pytest 固定装置只是糖和一点魔法是正确的.如果您发现它们很困难,则根本不需要使用它们.pytest 很高兴执行 vanilla unittest TestCases.
It's proper to note for posterity that pytest fixtures are just sugar and a bit of magic. You are not required to use them at all, if you find them difficult. pytest is happy to execute vanilla unittest TestCases.
另外请在答案中解释什么是最好的使用方法而不是屈服驱动程序实例.
Also please explain in the answer what is the best way to use instead of yield driver instance.
这就是我的想法......
Here's what I had in mind ...
@pytest.fixture(scope="class")
def oneTimeSetUp(request, browser):
print("Running one time setUp")
if browser == 'firefox':
driver = webdriver.Firefox()
driver.maximize_window()
driver.implicitly_wait(3)
print("Running tests on FF")
else:
driver = webdriver.Chrome()
print("Running tests on chrome")
if request.cls is not None:
request.cls.driver = driver
...请注意,这不会返回(或产生)驱动程序对象,这意味着将这个夹具作为命名参数提供给函数/方法不再有用,如果您的所有测试用例被编写为类(由您的示例建议).
... notice that this doesn't return (or yield) the driver object, which means that it's no longer useful to provide this fixture as a named parameter to a function/method, which should be fine if all of your test cases are written as classes (suggested by your examples).
但是,如果您想将夹具用作命名参数,请不要这样做.
However, if you want to use the fixture as a named parameter, don't do this.
这篇关于pytest->如何在类下的测试方法中使用夹具返回值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!