【问题标题】:How to create object from another object in Python unit test如何在 Python 单元测试中从另一个对象创建对象
【发布时间】:2026-01-25 15:30:01
【问题描述】:

我无法理解 Python 中的单元测试。我有一个对象retailer,它创建了另一个对象dealdeal 指的是在 retailer 中创建的一个属性,所以我将它传递给一个引用:

class deal():
    def __init__(self, deal_container, parent):

deal_container 属性也来自retailer,它调用自己的方法来创建它。那么如何创建轻松制作deal 对象所需的一切?

我是否必须在我的单元测试中创建retailer 的实例,然后调用该对象中创建deal 的方法?

我可以使用 FactoryBoy 创建retailer 的实例吗?如何在该对象中包含创建deal 的方法?

解决这个问题的最佳方法是什么?

这是单元测试。我正在设置soup_obj 我需要进行交易:

class TestExtractString(TestCase):
    fixtures = ['deals_test_data.json']

    def setUp(self):
        with open('/home/danny/PycharmProjects/askarby/deals/tests/BestBuyTest.html', 'r') as myfile:
            text = myfile.read().replace('\n', '')
        self.soup_obj = bs4.BeautifulSoup(text,"html.parser")
        self.deal = self.soup_obj.find_all('div',attrs={'class':'list-item'})[0]

    def test_extracts_title(self):
        z = Retailer.objects.get(pk=1)
        s = dealscan.retailer(z)
        d = dealscan.deal(self.deal,s)
        result = d.extract_string(self.deal,'title')

这是dealscandeal 类的相关位。有一个retailer 类创建了一个deal,但我什至还没有在retailer 中编写创建deal 的位。我希望我可以模拟deal 所需的位,而无需调用retailer,但是我该如何处理deal 引用retailer 的事实?

class deal():
    def __init__(self, deal_container, parent):
        '''
        Initializes deal object
        Precondition: 0 > price
        Precondition: 0 > old_price
        Precondition: len(currency) = 3

        :param deal_container: obj
        '''
        self.css = self.parent.css

        self.deal_container = deal_container
        self.parent = parent
        self.title = self.extract_string('title')
        self.currency = self.parent.currency
        self.price = self.extract_price('price')
        self.old_price = self.extract_price('old_price')
        self.brand = self.extract_string('brand')
        self.image = self.extract_image('image')
        self.description = self.extract_string('description')

        #define amazon category as clearance_url
        #define all marketplace deals

    def __str__(self):
        return self.title

    def extract_string(self, element, deal):
        '''

        :param object deal: deal object to extract title from
        :param string element: element to look for in CSS
        :return string result: result of string extract from CSS
        '''
        tag = self.css[element]['tag']
        attr = self.css[element]['attr']
        name = self.css[element]['name']
        result = deal.find(tag, attrs={attr: name})
        if result:
            if element == 'title':
                return result.text
            elif element == 'price':
                result = self.extract_price(result).text
                if result:
                    return result
            elif element == 'image':
                result = self.extract_image(result)

        return False

【问题讨论】:

  • 这些对象是如何创建的并不重要。单元测试应该关注deal 做了什么,它的行为是什么。所以你可以传入你喜欢的东西:真实对象、mock 对象或存根,只要测试可以确定交易行为是否正确。
  • 请出示您要测试的“单元”的代码,并说明测试的目标。
  • 没错。但是要测试交易的功能,我必须创建一个交易,它需要一个父代,因为它引用了父代拥有的属性。因此,如果我尝试在其父对象之外创建交易,我会得到:AttributeError:'deal' 对象没有属性'parent'。我必须创建“交易”,以免引发该错误。这意味着我需要创建一个父对象。
  • 根据@KlausD 的请求添加了代码。最终在单元测试中会有一个 self.assert 来检查它是否返回一个字符串,但现在我只是想让它阻止单元测试抛出错误 AttributeError: 'deal' object has no attribute '父母'

标签: python factory factory-boy


【解决方案1】:

问题是交易对象在设置 self.parent 属性之前引用了父对象。使用:

self.parent = parent        
self.css = self.parent.css
self.deal_container = deal_container

然后AttributeError 消失了。

关于在单元测试中使用一个对象来创建另一个对象是否是一种好形式的问题,答案是你可以使用mock,但是这样就可以了。在 setUp 中使用辅助方法设置父对象一次是可以接受的,这将使代码更易于阅读,并且可能会稍微提高测试性能。

【讨论】: