请强调,这不是重复的,因为我已阅读此处的所有 pytest 子类问题,但没有一个解决我遇到的错误。
我的问题简而言之:我对一个子类进行了 pytest,该子类失败了,因为测试依赖于超类属性来运行,但是 pytest 在子类的这些属性上引发了 AttributeError,而这些属性在超类中得到了很好的定义
我有两个类 Entity 一个基类,用于处理 id 生成和常见错误的错误处理 一旦发生业务规则验证错误,实体就会将其添加到错误列表中,并且在其 init 类中,如果错误列表不为空,则会引发 ValueError
实体.py
from abc import ABC
import uuid
# a util class containing basic validation bool functions. ex: is_valid_datetime
from util import Validator
class Entity(ABC):
id: str
errors: list[dict]
def __init_(self) -> None:
self.id = ""
self.errors = []
if not self.id:
self.id = uuid.uuid4().hex
self.validate()
def validate(self) -> None:
if len(self.errors) > 0:
raise ValueError(self.errors)
# simple example of a validation function
def validate_is_datetime(self, attr_name: str, attr_value: datetime) -> bool:
if Validator.is_valid_datetime(attr_value)
return True
else:
self.errors.append(
{
"type": "datetime",
"loc": f"Entity, {attr_name}",
"msg": "Attribute should be a valid datetime",
"input": attr_value
}
)
继承自实体的 Product 类。 产品.py
from entity import Entity
class Product(Entity):
name: str
price: str
def __init__(name: str, price: str) -> None:
self.name = name
# example of a validation that will fail during instantiation
# I need it to fail to showcase the problem I'm having
self.validate_is_datetime(name)
self.price = price
super().__init__()
使用 pytest
测试我的课程Pytest 装置 conftest.py
import pytest
from entity import Entity
from product import Product
@pytest.fixture(scope="class")
def entity():
return Entity()
@pytest.fixture(scope="class")
def product():
return Product(name="Iphone 8", price="150")
测试产品域实体 test_product.py
# simple test to ensure correct instantiation
def test_name_is_iphone8(product: Product) -> None:
assert product.name == "Iphone 8"
我首先在 test_product.py 中运行产品模型的测试
pytest
现在显然按照之前的定义,我预计测试会失败,因为在产品实例化期间会引发 ValueError,正如我在产品 init `self.validate_is_valid_datetime(self.name), name: str 中调用的那样,name: str 不能是有效的日期时间。
结果是测试失败,好的,但不会引发 ValueError,而是引发 AttributeError,指出 product 对象没有错误属性。因此 pytest 不会出现错误是在超类 Entity 中定义的,并且应该在继承它的 Product 中可用。
抱歉,我发现了我犯的错误(这个项目有点太仓促了):
在 Product init 方法中,我在 Product.
init() 函数末尾调用
super().__init__()
。
class Product(Entity):
...
def __init__(name: str, price: str) -> None:
self.name = name
#the test that fails
self.validate_is_datetime(self.name) #this function depends on errors attr that has yet to be initialized by the super class
self.price = price
super().__init__() # The errors attr is only initialized at this point so all functions using before this call will raise AttributeError
虽然应该更快地调用它,因为实体 init 方法会初始化错误属性。在Python中,如果类成员未初始化,Python在调用它时将无法识别它。所以product.py init 方法的代码应该是:
class Product(Entity):
...
def __init__(name: str, price: str) -> None:
# call it first to initialize errors attr needed in validation methods
super().__init__()
self.name = name
#the test that fails
self.validate_is_datetime(self.name)
self.price = price
现在我怀疑其他人会犯同样的愚蠢错误,但是如果您在测试子类时遇到 pytest 属性错误,请确保您的属性已使用值很好地初始化,单独定义它(例如:name:str)是行不通的。 如果您的 attr 在超类中初始化,请确保在适当的时间调用它的 init 方法,以在使用所需属性之前对其进行初始化