代码之家  ›  专栏  ›  技术社区  ›  ivan_pozdeev RenanSS

模拟一系列相互依赖的调用

  •  0
  • ivan_pozdeev RenanSS  · 技术社区  · 5 年前

    我有一个方法,它可以抓取网页并将数据保存到文件中(参见下面的示例代码)。我需要测试结果数据的格式是否正确。

    问题是,数据是从一系列调用中接收到的,而进一步的调用使用以前调用的结果。更糟糕的是,涉及的许多调用都是在同一对象上进行的(a Webdriver A WebDriverWait 以及 expected_conditions 模块),参数不同。

    我明白了 unittest.mock.Mock 可以模拟一个简单调用或一系列简单调用的结果,但看不到如何实现这样的纠缠。我看到的唯一方法是手动重新实现方法所做的每个调用,并将方法中传递的参数复制到这些实现中,以便它们知道每次调用返回什么。对于其他的测试用例也要这样做。这听起来像是编写和维护的一场噩梦:代码数量是测试本身的几倍,与代码的重复率接近1:1。所以我拒绝继续,直到有人告诉我有更好的方法或者证明没有,并且每个人都这样做(我不相信),例如每次页面上的标签更改时重写所有测试(这是一个实现细节,所以通常不应该影响测试代码)。

    样本代码(适用于 http://example.com ):

    import selenium.webdriver
    from selenium.webdriver.common.by import By as by
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.support.ui import WebDriverWait
    
    
    def dump_accreditation_data(d, w, i, path):
        f = codecs.open(os.path.join(path, "%d.txt" % i), "w", encoding="utf-8")
    
        u = u'http://example.com/%s/accreditation' % i
        d.get(u)
    
        # page load
        w.until(EC.visibility_of_element_located((by.XPATH,"//p")))    #the real code has a more complex expression here with national characters
        w.until_not(EC.visibility_of_element_located((by.CSS_SELECTOR, '.waiter')))
        print >> f, u
    
        # organization name
        e = w.until(EC.visibility_of_element_located((
            by.CSS_SELECTOR, 'h1'
        )))
        org_name = e.text
        print >> f, org_name
        del e
    
        #etc
        e = d.find_element_by_xpath(u'//a[text()="More information..."')
        print >> f, e.get_attribute('href')
    
    #How it's supposed to be used:
    d = selenium.webdriver.Firefox()
    w = WebDriverWait(d, 10)
    dump_accreditation_data(d, w, 123, "<output_path>")
    
    1 回复  |  直到 5 年前
        1
  •  1
  •   Dirk Herrmann    5 年前

    对于代码来说,我同意,以您描述的方式进行单元测试没有多大意义。但是,这不仅仅是因为它需要大量的工作:测试的目标当然是在代码中发现错误。单元测试的目标是找到那些可以在隔离单元中找到的错误。但是,示例代码的一个重要部分与与外部库的交互有关。

    在算法级别上几乎没有代码,例如:

    os.path.join(path, "%d.txt" % i)
    

    u = u'http://example.com/%s/accreditation' % i
    

    或者创建输出文件内容。

    也就是说,如果代码中存在错误,它们更可能处于交互级别:使用正确的参数、具有正确格式的参数等以正确的顺序调用正确的库函数。-但是,对于库的模拟,您将找不到交互错误,因为模拟是由您和Will J实现的。必须反映您(可能是错误的)对库行为的理解。

    我对测试此代码的建议是:将算法代码与与与库进行交互的代码分开。例如,您可以创建小助手函数来计算输出文件名和输入URL。您可以在代码中以交互为主的部分,从网页中提取所有数据,然后(在单独的函数中)使用所有这些数据创建输出文件内容。

    然后可以使用单元测试来测试这些助手函数。您将用集成测试测试的其余功能。