为什么这个方法被调用两次(pytest)

why is this method being called twice (pytest)

我在 models.py 中有一个名为 Tree 的 class 和一个名为 Property 的 class:

class Tree:
    def __init__(self, name=None):
        self.name = name
        self.properties = []

    def get_name(self):
        return self.name

    def add_property(self, prop):
        if prop.__class__.__name__ != 'Property':
            raise TypeError('Only properties may be added as properties')
        else:
            print('adding property: ' + str(prop))
            self.properties.append(prop)
            
    def add_properties(self, arr):
        for label in arr:
            property = Property(label=label)
            self.add_property(property)

    def get_properties(self):
        return self.properties

class Property:
    def __init__(self, label=None):
        self.label = label
        self.tree = None
        self.values = []


    def __str__(self):
        return self.label

我正在尝试使用以下 pytest 测试脚本测试这些 classes,该脚本从 .feature 文件获取输入:

import pytest
from pytest_bdd import scenario, given, when, then, parsers
from models import Tree

@scenario('../features/Tree.feature',
          'add properties to a tree')
def test_tree():
    pass

@given("a new tree is created", target_fixture="create_tree")
def create_tree():
    return Tree()

@pytest.fixture()
@when(parsers.cfparse("{properties} are added as properties"))
def add_properties(create_tree, properties):
    properties = properties.split(',')
    create_tree.add_properties(properties)
    return create_tree

@then("I can get these properties")
def publish_article(add_properties):
    result = add_properties.get_properties()
    assert len(result) == 3

Tree.feature

Feature: Tree

# BUILD TOP-DOWN 

Scenario: add properties to a tree
  Given a new tree is created
  When height, weight, age are added as properties
  Then I can get these properties

不知何故,when 步骤中的 add_properties 方法被调用了两次。我知道这是因为打印了以下内容:

add_properties = <models.Tree object at 0x10b792760>

    @then("I can get these properties")
    def publish_article(add_properties):
        print(add_properties.get_properties())
>       assert len(add_properties.get_properties()) == 3
E       assert 6 == 3
E        +  where 6 = len([<models.Property object at 0x10b792b50>, <models.Property object at 0x10b792a00>, <models.Property object at 0x10b792...dels.Property object at 0x10b62a280>, <models.Property object at 0x10b62a040>, <models.Property object at 0x10b62a0d0>])
E        +    where [<models.Property object at 0x10b792b50>, <models.Property object at 0x10b792a00>, <models.Property object at 0x10b792...dels.Property object at 0x10b62a280>, <models.Property object at 0x10b62a040>, <models.Property object at 0x10b62a0d0>] = <bound method Tree.get_properties of <models.Tree object at 0x10b792760>>()
E        +      where <bound method Tree.get_properties of <models.Tree object at 0x10b792760>> = <models.Tree object at 0x10b792760>.get_properties

tests/step_defs/test_tree.py:26: AssertionError
----------------------------- Captured stdout call -----------------------------
adding property: height
adding property:  weight
adding property:  age
adding property: height
adding property:  weight
adding property:  age
[<models.Property object at 0x10b792b50>, <models.Property object at 0x10b792a00>, <models.Property object at 0x10b792a60>, <models.Property object at 0x10b62a280>, <models.Property object at 0x10b62a040>, <models.Property object at 0x10b62a0d0>]
=========================== short test summary info ============================
FAILED tests/step_defs/test_property.py::test_property - pytest_bdd.exception...
FAILED tests/step_defs/test_tree.py::test_tree - assert 6 == 3
========================= 2 failed, 1 passed in 0.24s ========================== 

为什么要添加两次属性?

我认为问题在于 pytest_bdd 允许您将 @given 用作固定装置,但您还在 @when 上添加了 @pytest.fixture 装饰器。

这意味着 add_properties 函数作为 when 步骤调用一次,然后作为 pytest fixture 再次调用。删除额外的装饰器并使用原来的 create_tree fixture。

import pytest
from pytest_bdd import scenario, given, when, then, parsers
from models import Tree

@scenario('../features/Tree.feature',
          'add properties to a tree')
def test_tree():
    pass

@given("a new tree is created", target_fixture="create_tree")
def create_tree():
    return Tree()

@when(parsers.cfparse("{properties} are added as properties"))
def add_properties(create_tree, properties):
    properties = properties.split(',')
    create_tree.add_properties(properties)
    return create_tree

@then("I can get these properties")
def publish_article(create_tree):
    result = create_tree.get_properties()
    assert len(result) == 3