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

Mock a series of interdependent calls

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

问题是,数据是从一系列调用中接收到的,并且进一步的调用使用了先前调用的结果。更糟糕的是,涉及的许多调用是在相同的对象(WebdriverWebDriverWaitexpected_conditions 模块上完成的,具有不同的参数。

我看到 unittest.mock.Mock 可以模拟一个简单调用或一系列简单调用的结果,但看不出如何实现像这样纠结的东西。我看到的唯一方法是手动重新实现该方法进行的每次调用,并将我在该方法中传递的参数复制到这些实现中,以便它们知道每次调用要 return 什么。并为所有其他测试用例再次执行此操作。这听起来像是编写和维护的绝对噩梦:代码比测试本身多几倍,并且几乎 1:1 重复代码。所以我拒绝继续,直到有人告诉我有更好的方法或证明有 none 并且每个人都真的这样做(我不相信),例如每次页面上的标签更改时都会重写所有测试(这是一个实现细节,所以通常情况下,它根本不应该影响测试代码)。

示例代码(适用于 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>")

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

算法层面的代码比较少,例如:

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

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

或者创建输出文件内容。

也就是说,如果代码中存在错误,则它们更有可能出现在交互级别:使用正确的参数以正确的顺序调用正确的库函数,具有正确格式的参数等。 - 与库的模拟,但是,您不会发现交互错误,因为模拟是由您实现的,只会反映您(可能是错误的)对库行为的理解。

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

然后可以使用单元测试对这些辅助函数进行全部测试。您将使用集成测试测试的其余功能。