【问题标题】:How to test complicated functions which use requests?如何测试使用请求的复杂功能?
【发布时间】:2019-04-11 15:03:54
【问题描述】:

我想测试基于其他人创建的 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 非常大,而且过程会很长。

【问题讨论】:

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


    【解决方案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 的模拟版本,而无需从文件中读取数据 - 可以完全在单元测试代码中设置模拟。

    【讨论】:

      【解决方案2】:

      尝试修补模块中的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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-14
        • 1970-01-01
        • 1970-01-01
        • 2016-07-24
        • 1970-01-01
        相关资源
        最近更新 更多