用Mock编写Python2.7 unittest(URLError:<urlopen error =“”unknown =“”url =“”type:=“”http =“”>)

时间:2017-03-08 10:17:55

标签: python unit-testing mocking

几天来一直在努力为我的代码制作一些测试用例。 我编写了一个代码来批量下载带有给定输入的文件,其形式为URL字符串或URL字符串列表。我的代码目前支持4种协议(http,https,ftp,sftp)

我一直在阅读有关Mock的内容并观看了一些视频。我仍然无法将我在互联网上看到的样本应用到我的实际代码中。 :(

下面是我的批量下载实现和我写的单元测试仍然失败:

def batch_download(url):
 req = Request(url)
try:
    print '\nStart downloading...'
    response = urlopen(req)
except URLError as e:
    # No network connection or Invalid URL or The specified server doesn't exist.
    if hasattr(e, 'reason'):
        print 'We failed to reach a server.'
        print 'Reason: ', e.reason
    # HTTP Response that is not 200.
    elif hasattr(e, 'code'):
        print 'The server couldn\'t fulfill the request.'
        print 'Error code: ', e.code
else:
    # Retrieve a redirected URL, if there is one.
    real_url = response.geturl()
    print real_url
    saved_to = get_local_path(real_url)
    urlretrieve(real_url, saved_to)

    # meta_data = response.info()['Content-Length']
    # file_on_disk = os.stat(saved_to).st_size
    # print '\nContent-Length: ' + meta_data
    # print 'File on Disk after Download: ' + str(file_on_disk)
    remove_partially_downloaded(real_url, response, saved_to)

    urlcleanup()
return



def remove_partially_downloaded(url, response, local_path_to_file):
 meta_data = response.info()['Content-Length']
 file_on_disk = os.stat(local_path_to_file).st_size
 print '\nContent-Length: ' + meta_data
 print 'File on Disk after Download: ' + str(file_on_disk)

 partial_download = int(meta_data) - file_on_disk

 if partial_download > 0:
    print '\nThe following partially downloaded file ' + get_filename_from_url(url) + ' will be removed'
    os.remove(local_path_to_file)
 else:
    return

格式化单元测试代码时出现问题。所以我在这里附上了我的unittest类的图像 enter image description here

我得到的失败信息如下 enter image description here

感谢任何帮助。有关使用补丁和MagicMock的建议非常受欢迎。我知道这两者的基本概念,但仍然无法弄清楚将这些概念纳入我的代码的位置。我对此非常陌生。

1 个答案:

答案 0 :(得分:1)

好吧,我认为有两个阻挡者在场。首先,您执行的不仅仅是代码。诸如urllib2之类的依赖关系未被正确隔离。实际上,该库的代码中引发了错误。其次,目前尚不清楚你要测试的是什么。为了解决这个问题,我建议使用AAA pattern来编写测试。

让我们从第二个问题开始吧。您希望测试有3个部分:

  • 排列。在第一部分中,您将设置执行代码所需的一切。这包括创建模拟并指导它们按照您的意愿做出响应。
  • 法。现在,您可以调用要测试的代码单元,在本例中为batch_download。
  • 断言。验证代码的输出是否等于预期结果,或验证模拟是否按预期调用。理想情况下,您应该在本节中只包含一个断言,因此每次测试只测试一个断言。

在测试中,您在设置模拟之前调用batch_download。然后,您设置了一些模拟(虽然不正确,见下文),然后调用代码的另一个函数。没有断言。

可能很难为您的方法提出一个简单的AAA,一个断言测试。那是因为你的功能做了很多事情。尝试重构几个函数,每个函数都做一个可直接测试的东西。 (我强烈建议您查看Robert Martin的书“清洁代码”,它提供了一些提高代码质量的技巧和指南。)

至于嘲笑。你不应该使用urllib2开启者。首先,因为那时您将测试该库以及您的代码。测试成为一种集成测试,并且它不是很好,因为它引入了原始方法中没有的新逻辑(额外的失败点)。其次,更容易使用模拟库。

模拟该依赖的简单方法是使用补丁。举个例子,让我们测试如果urlopen没有引发Error(返回响应),并且在响应上调用了geturl,那么将使用期望的url和save_to路径调用urlretrive:

import mock
...
@mock.patch('test_obj.urllib2')
def test_batch_download(self, urllib2_mock):
    # Arrange:
    response_mock = mock.MagicMock()
    real_url = 'http://sample.com'
    response_mock.geturl.return_value = real_url
    urllib2_mock.urlopen.return_value = response_mock

    # Act:
    test_obj.batch_download('http://originalurl.com')

    # Assert:
    expected_save_to_path = '/var/dl/sample.com'
    urllib2_mock.urlretrive.assert_called_once_with(real_url, expected_save_to_path)

我希望这会有所帮助。祝你好运!