模拟一系列相互依赖的调用 [英] Mock a series of interdependent calls

查看:36
本文介绍了模拟一系列相互依赖的调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a method that scrapes a web page and saves data into a file (see below for an example code). I need to test that the resulting data is well-formed.

问题是,数据是从一系列调用中接收到的,后续调用会使用之前调用的结果.更糟糕的是,涉及的许多调用都是在相同的对象(WebdriverWebDriverWaitexpected_conditions 模块)上完成的,具有不同的参数.

The problem is, the data is received from a series of calls, and further calls use the results of previous ones. What is worse, many of the calls involved are done on the same objects (a Webdriver, a WebDriverWait and the expected_conditions module), with different arguments.

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

I see that unittest.mock.Mock can mock the result of a simple call, or a series of simple calls, but can't see how to implement something entangled like this. The only way I see is to manually reimplement each and every call the method makes, and copy the arguments I pass in the method into those implementations so that they know what to return for each call. And do that again for every other test case. This sounds like an absolute nightmare to write and maintain: several times more code than the tests themselves and near 1:1 duplication with the code. So I refuse to proceed until someone tells me that there's a better way or proves that there is none and everyone really does it like this (which I don't believe) and e.g. rewrites all the tests each time a label on the page changes (which is an implementation detail, so normally, it shouldn't affect test code at all).

示例代码(适用于 http://example.com):

Sample code (adapted for 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>")

推荐答案

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

For the code as it is, I agree, unit-testing the way you describe does not make much sense. But, that is not just because it would be a lot of work: The goal of testing is, certainly, to find errors in the code. The goal of unit-testing is, to find those errors that can be found in the isolated unit. But, a significant part of your example code is related to interaction with external libraries.

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

There is comparably little code on the algorithmic level, for example:

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

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

或创建输出文件内容.

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

That is, if there are bugs in the code, they are more likely to be on the interaction level: Calling the right library functions in the right order with the right parameters, parameters having the correct formats etc. - With mocks of the libraries, however, you will not find the interaction bugs, because the mocks are implemented by you and will just reflect your (potentially wrong) understanding of the library behaviour.

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

My suggestion for testing this code is: Separate the algorithmic code from the code that does the interaction with the libraries. You could, for example, create small helper functions to compute the output file name and the input url. You could, in the interaction dominated part of the code, extract all the data from the web page, and then (in a separate function) create the output file content using all that data.

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

These helper functions can then all be tested using unit-testing. The rest of the functionality you would test with integration testing.

这篇关于模拟一系列相互依赖的调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆