如何测试使用请求的复杂功能?

时间:2019-04-11 15:03:55

标签: python python-3.x unit-testing pytest

我想测试基于其他人创建的API的代码,但是我不确定该怎么做。

我创建了一些函数来将json保存到文件中,因此我不需要每次运行测试时都发送请求,但是我不知道如何在原始(检查)函数需要的情况下使其工作输入arg(problem_report),它是API提供的某个类的实例,并且具有 problem_report.get_correction(corr_link)方法。我只是想知道这是否是我编写的代码不好的征兆,因为我无法对此进行测试,或者我应该像下面提供的代码结尾所示那样在我的测试文件中重写此功能。

# I to want test this function
def check(problem_report): 
    corrections = {}
    for corr_link, corr_id in problem_report.links.items():
        if re.findall(pattern='detailCorrection', string=corr_link):
            correction = problem_report.get_correction(corr_link)
            corrections.update({corr_id: correction})
    return corrections

# function serves to load json from file, normally it is downloaded by API from some page.
def load_pr(pr_id): 
    print('loading')
    with open('{}{}_view_pr.json'.format(saved_prs_path, pr_id)) as view_pr:
        view_pr = json.load(view_pr)
    ...
    pr_info = {'view_pr': view_pr, ...}
    return pr_info

# create an instance of class MyPR which takes json to __init__
@pytest.fixture
def setup_pr():
    print('setup')
    pr = load_pr('123')
    my_pr = MyPR(pr['view_pr'])
    return my_pr 

# test function
def test_check(setup_pr):
    pr = setup_pr
    checked_pr = pr.check(setup_rft[1]['problem_report_pr'])
    assert checker_pr

# rewritten check function in test file 
@mock.patch('problem_report.get_correction', side_effect=get_corr)
def test_check(problem_report):
    corrections = {}
    for corr_link, corr_id in problem_report.links.items():
        if re.findall(pattern='detailCorrection', string=corr_link):
            correction = problem_report.get_correction(corr_link)
            corrections.update({corr_id: correction})
    return corrections

我不确定是否提供了足够的代码和说明来理解该问题,但我希望如此。我希望您能告诉我是否正常,某些功能很难测试,并且如果这是将它们分别改写的好习惯,那么我可以在测试的函数中模拟功能。我还以为我可以编写具有类似功能的新类,但API很大,而且过程很长。

2 个答案:

答案 0 :(得分:1)

我理解您的问题如下:您有一个功能check,由于它对problem_report的依赖性而使您难以测试。为了使其更易于测试,您已将代码复制到测试文件中。您将测试复制的代码,因为您可以对其进行修改以使其更易于测试。而且,您想知道这种方法是否有意义。

答案是否定的,这没有道理。您不是要测试实际功能,而是要测试完全不同的代码。好的,代码可能不会完全不同,但是在很短的时间内,副本和原始版本将有所不同,这将是维护噩梦,以确保副本始终与原始版本相似。改善代码的可测试性是另外一回事:您可以更改check函数以提高其可测试性。但是,然后,在测试和生产代码中都应使用完全相同的结果函数。

然后如何更好地测试功能check?首先,您确定在测试中确实无法合理地使用原始problem_report对象吗? (以下标准可帮助您做出决定:What to mock for python test cases?)。现在,假设您得出的结论是您无法明智地使用原始的problem_report

在这种情况下,这里的接口足够简单,可以定义模拟的problem_report。请记住,Python使用鸭子类型,因此您只需要创建一个具有links成员且具有items()方法的类。另外,您的模拟problem_report类需要一个方法get_correction()。除此之外,您的模拟不必产生与problem_report使用的类型相似的类型。 items()方法可以仅返回列表列表,例如[["a",2],["xxxxdetailCorrectionxxxx",4]]get_correction使用相同的参数,例如可以简单地返回其参数或派生值(例如其负数)。

对于上述示例(items()返回[["a",2],["xxxxdetailCorrectionxxxx",4]],而get_correction返回其参数的负数),预期结果将为{4: -4}。无需模拟真实的correction对象。而且,您可以创建problem_report的模拟版本,而无需从文件中读取数据-可以从单元测试代码中完全设置模拟。

答案 1 :(得分:0)

尝试修补模块中的problem_report符号。您应该将测试放在单独的类中。

@mock.patch('some.module.path.problem_report')
def test_check(problem_report):
    problem_report.side_effect = get_corr
    corrections = {}
    for corr_link, corr_id in problem_report.links.items():
        if re.findall(pattern='detailCorrection', string=corr_link):
            correction = problem_report.get_correction(corr_link)
            corrections.update({corr_id: correction})
    return corrections