代码之家  ›  专栏  ›  技术社区  ›  Piccolo

如何使用请求模拟对文件上载进行单元测试?

  •  2
  • Piccolo  · 技术社区  · 8 年前

    我正在为一个API编写一个Python(3)包装器,并尝试对其中需要上传文件的部分进行单元测试。我想验证文件名和内容是否由我的客户正确发送。

    我在用Python的 unittest 图书馆,还有 requests requests_mock 为了测试这个。

    我计划解决这个问题的方法是使用一个回调函数来验证文件是否已发送,并且所有的头都已正确设置。以下是我目前掌握的情况:

    import unittest
    import requests
    import requests_mock
    
    from my_class import my_class
    
    from my_class.API import API
    
    class  TestAPI(unittest.TestCase):
    
        def setUp(self):
            self.hostname = 'https://www.example.com'
    
        def validate_file_upload(self, request, context, filename, content):
            # self.assertEqual(something, something_else)
            # better solution goes here
    
        def test_submit_file(self):
            API_ENDPOINT = self.hostname + '/api/tasks/create/file/'
            DUMMY_FILE = 'file'
            DUMMY_CONTENT = 'here is the\ncontent of our\nfile'
    
            s = API(self.hostname)
    
            with open(DUMMY_FILE, 'w+') as f:
                f.write(DUMMY_CONTENT)
    
            with requests_mock.Mocker() as m:
                def json_callback(request, context):
                    self.validate_file_upload(request, context, DUMMY_FILE,
                        DUMMY_CONTENT)
                    return {}
    
                m.post(API_ENDPOINT, json=json_callback)
                s.upload_file(DUMMY_FILE)
    

    我已经确定,在成功上传文件后 request 参数到 validate_file_upload 有几个相关的数据位,即 request.headers request.text . 以下是他们两人在 验证文件上载 函数被调用:

    请求头

    {'User-Agent': 'python-requests/2.19.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '171', 'Content-Type': 'multipart/form-data; boundary=e1a0aa05f83735e85ddca089c450a21b'}
    

    请求.text

    '--e1a0aa05f83735e85ddca089c450a21b\r\nContent-Disposition: form-data; name="file"; filename="file"\r\n\r\nhere is the\ncontent of our\nfile\r\n--e1a0aa05f83735e85ddca089c450a21b--\r\n'
    

    现在,事情是这样的。我 知道我可以解析 这个 请求.text 字符串并获取我想要的数据;这很容易验证。

    然而,这种逻辑似乎真的不属于我的单元测试。我无法想象没有更好的解决方案;要么有人已经在另一个模块中实现了这个功能,要么我忽略了一些显而易见的东西。

    我不应该执行 HTTP spec 用于文件上载 对文件上传这样简单的东西进行单元测试,对吧?有更好的办法吗?


    以下是 dir(request) :

    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_allow_redirects', '_case_sensitive', '_cert', '_create', '_matcher', '_proxies', '_qs', '_request', '_stream', '_timeout', '_url_parts', '_url_parts_', '_verify', 'allow_redirects', 'cert', 'hostname', 'json', 'matcher', 'netloc', 'path', 'port', 'proxies', 'qs', 'query', 'scheme', 'stream', 'text', 'timeout', 'verify']
    

    我已经检查了所有非下划线属性,以获取文件上传数据的任何其他表示形式,但没有结果。我也试过搜索 StackOverflow Google ,而且我离找到更好的方法也不远了。这是两个搜索中唯一出现的帖子。

    1 回复  |  直到 8 年前
        1
  •  1
  •   Adam Dangoor    6 年前

    我没有我的_class.API来测试您的确切代码,但是我相信您可以使用 cgi

    import cgi
    
    body_file = io.BytesIO(request.body)
    _, pdict = cgi.parse_header(request.headers['Content-Type'])
    parsed = cgi.parse_multipart(fp=body_file, pdict=pdict)
    # Now inspect `parsed`
    
        2
  •  0
  •   Piccolo    8 年前

    目前,我决定采取这种相当简单的方法:

    def validate_file_upload(self, request, context, filename, content):
        self.assertTrue(filename in request.text)
        self.assertTrue(content in request.text)
    

    虽然它并不完美,但它的逻辑性明显低于解析HTTP请求,而且似乎至少对文件上传是否正确进行了基本验证。如前所述,我正在使用 requests 库,所以我不太担心文件上传会弄乱,不管怎样,在大多数情况下,这都会抓住它。

    为了防止名称不正确匹配 file 反对其他的东西 request.text ,我已将其更改为 rather_unique_filename .