Python 3.x 使用Flask测试客户端上载测试文件

Python 3.x 使用Flask测试客户端上载测试文件,python-3.x,flask,werkzeug,flask-testing,Python 3.x,Flask,Werkzeug,Flask Testing,我正在测试两个视图,这两个视图都使用对象保存post请求中上传的文件 在测试过程中,我不想实际保存上传的存储,所以我使用unittest的补丁decorator对FileStorage.save方法进行了修补 测试#1: 测试#2: 我正在测试的一个视图如下所示: 如果我只运行其中一个测试,它可以正常工作,但是当我添加第二个测试时,运行失败,出现以下错误: ___________________ TestDataFileList.test_create_data_file ___________

我正在测试两个视图,这两个视图都使用对象保存post请求中上传的文件

在测试过程中,我不想实际保存上传的存储,所以我使用
unittest
补丁
decorator对
FileStorage.save
方法进行了修补

测试#1: 测试#2: 我正在测试的一个视图如下所示: 如果我只运行其中一个测试,它可以正常工作,但是当我添加第二个测试时,运行失败,出现以下错误:

___________________ TestDataFileList.test_create_data_file ____________________

self = <test.test_api.TestDataFileList testMethod=test_create_data_file>
fs_save_mock = <MagicMock name='save' id='268959928'>

    @patch.object(FileStorage, 'save')
    def test_create_data_file(self, fs_save_mock):
        """Test creation/upload of a data data file."""
        file_storage = FileStorage(
            name='Test Storage', filename='teststorage.ext')
        data = {'file': file_storage}

>       rv = self.client.post('/api/data/upload', data=data)

test\test_api.py:469:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\venv\lib\site-packages\werkzeug\test.py:840: in post
    return self.open(*args, **kw)
..\venv\lib\site-packages\flask\testing.py:192: in open
    environ = builder.get_environ()
..\venv\lib\site-packages\werkzeug\test.py:588: in get_environ
    stream_encode_multipart(values, charset=self.charset)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

values = [<FileStorage: 'teststorage.ext' (None)>], use_tempfile = True
threshold = 512000
boundary = '---------------WerkzeugFormPart_1534924385.40363360.05943907979559804'
charset = 'utf-8'

    def stream_encode_multipart(values, use_tempfile=True, threshold=1024 * 500,
                                boundary=None, charset='utf-8'):
        """Encode a dict of values (either strings or file descriptors or
        :class:`FileStorage` objects.) into a multipart encoded string stored
        in a file descriptor.
        """
        if boundary is None:
            boundary = '---------------WerkzeugFormPart_%s%s' % (time(), random())
        _closure = [BytesIO(), 0, False]

        if use_tempfile:
            def write_binary(string):
                stream, total_length, on_disk = _closure
                if on_disk:
                    stream.write(string)
                else:
                    length = len(string)
                    if length + _closure[1] <= threshold:
                        stream.write(string)
                    else:
                        new_stream = TemporaryFile('wb+')
                        new_stream.write(stream.getvalue())
                        new_stream.write(string)
                        _closure[0] = new_stream
                        _closure[2] = True
                    _closure[1] = total_length + length
        else:
            write_binary = _closure[0].write

        def write(string):
            write_binary(string.encode(charset))

        if not isinstance(values, MultiDict):
            values = MultiDict(values)

        for key, values in iterlists(values):
            for value in values:
                write('--%s\r\nContent-Disposition: form-data; name="%s"' %
                      (boundary, key))
                reader = getattr(value, 'read', None)
                if reader is not None:
                    filename = getattr(value, 'filename',
                                       getattr(value, 'name', None))
                    content_type = getattr(value, 'content_type', None)
                    if content_type is None:
                        content_type = filename and \
                            mimetypes.guess_type(filename)[0] or \
                            'application/octet-stream'
                    if filename is not None:
                        write('; filename="%s"\r\n' % filename)
                    else:
                        write('\r\n')
                    write('Content-Type: %s\r\n\r\n' % content_type)
                    while 1:
>                       chunk = reader(16384)
E                       ValueError: I/O operation on closed file.

..\venv\lib\site-packages\werkzeug\test.py:96: ValueError
===================== 1 failed, 31 passed in 3.89 seconds =====================
测试数据文件列表。测试创建数据文件____________________
自我=
fs_保存_模拟=
@对象(文件存储,“保存”)
def test_create_data_文件(自我、fs_save_mock):
“”“测试数据文件的创建/上载。”“”
文件存储=文件存储(
name='Test Storage',filename='teststorage.ext')
数据={'file':文件存储}
>rv=self.client.post('/api/data/upload',data=data)
test\test\u api.py:469:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\venv\lib\site packages\werkzeug\test.py:840:in post
返回自开(*args,**kw)
..\venv\lib\site packages\flask\testing.py:192:打开
environ=builder.get_environ()
..\venv\lib\site packages\werkzeug\test.py:588:在get_环境中
流编码多部分(值,charset=self.charset)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
值=[],使用_tempfile=True
阈值=512000
边界='----WerkzeugFormPart_1534924385.40363360.059439079559804'
字符集='utf-8'
def stream_encode_multipart(值,使用_tempfile=True,阈值=1024*500,
边界=无,字符集='utf-8'):
“”“对值(字符串或文件描述符或
:class:`FileStorage`objects.)转换为存储的多部分编码字符串
在文件描述符中。
"""
如果边界为“无”:
boundary='--------------WerkzeugFormPart\uU%s%s'(time(),random())
_闭包=[BytesIO(),0,False]
如果使用临时文件:
def write_二进制(字符串):
流,总长度,在磁盘上=\u闭包
如果在U盘上:
stream.write(字符串)
其他:
长度=长度(字符串)
如果长度+_闭包[1]区块=读取器(16384)
E ValueError:对关闭的文件执行I/O操作。
..\venv\lib\site packages\werkzeug\test.py:96:ValueError
============================1失败,在3.89秒内通过31次=====================

不知道如何解决这个问题,你们中有谁能帮我解决这个问题吗?

什么是
fs\u save\u mock
目的以及如何以及为什么要将它传递给测试函数?
fs\u save\u mock
正在模拟
文件存储。save
方法。正如您在视图中所看到的那样,将调用
file\u storage.save(filepath)
,并且在不模拟的情况下将文件保存到文件系统。
@patch.object(FileStorage, 'save')
def test_create_data_file(self, fs_save_mock):
    """Test creation/upload of a data data file."""
    file_storage = FileStorage(
        name='Test Storage', filename='teststorage.ext')
    data = {'file': file_storage}

    rv = self.client.post('/api/data/upload', data=data)
    json_data = rv.get_json()
    datafile = DataFile.query.get(json_data['id'])

    fs_save_mock.assert_called_once_with(
        generate_upload_filepath(file_storage.filename))
@blueprint.route('/upload', methods=['POST'])
@use_kwargs({'file_storage': fields.Field(location='files', load_from='file')})
def upload_datafile(file_storage):
    """Upload a datafile."""
    if file_storage is missing:
        abort(400, 'No file found in the request.')

    filepath = generate_upload_filepath(file_storage.filename)
    file_storage.save(filepath)

    data_file = DataFile.create(filename=file_storage.filename, path=filepath)
    return jsonify(DataFileSchema().dump(data_file).data), 201
___________________ TestDataFileList.test_create_data_file ____________________

self = <test.test_api.TestDataFileList testMethod=test_create_data_file>
fs_save_mock = <MagicMock name='save' id='268959928'>

    @patch.object(FileStorage, 'save')
    def test_create_data_file(self, fs_save_mock):
        """Test creation/upload of a data data file."""
        file_storage = FileStorage(
            name='Test Storage', filename='teststorage.ext')
        data = {'file': file_storage}

>       rv = self.client.post('/api/data/upload', data=data)

test\test_api.py:469:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\venv\lib\site-packages\werkzeug\test.py:840: in post
    return self.open(*args, **kw)
..\venv\lib\site-packages\flask\testing.py:192: in open
    environ = builder.get_environ()
..\venv\lib\site-packages\werkzeug\test.py:588: in get_environ
    stream_encode_multipart(values, charset=self.charset)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

values = [<FileStorage: 'teststorage.ext' (None)>], use_tempfile = True
threshold = 512000
boundary = '---------------WerkzeugFormPart_1534924385.40363360.05943907979559804'
charset = 'utf-8'

    def stream_encode_multipart(values, use_tempfile=True, threshold=1024 * 500,
                                boundary=None, charset='utf-8'):
        """Encode a dict of values (either strings or file descriptors or
        :class:`FileStorage` objects.) into a multipart encoded string stored
        in a file descriptor.
        """
        if boundary is None:
            boundary = '---------------WerkzeugFormPart_%s%s' % (time(), random())
        _closure = [BytesIO(), 0, False]

        if use_tempfile:
            def write_binary(string):
                stream, total_length, on_disk = _closure
                if on_disk:
                    stream.write(string)
                else:
                    length = len(string)
                    if length + _closure[1] <= threshold:
                        stream.write(string)
                    else:
                        new_stream = TemporaryFile('wb+')
                        new_stream.write(stream.getvalue())
                        new_stream.write(string)
                        _closure[0] = new_stream
                        _closure[2] = True
                    _closure[1] = total_length + length
        else:
            write_binary = _closure[0].write

        def write(string):
            write_binary(string.encode(charset))

        if not isinstance(values, MultiDict):
            values = MultiDict(values)

        for key, values in iterlists(values):
            for value in values:
                write('--%s\r\nContent-Disposition: form-data; name="%s"' %
                      (boundary, key))
                reader = getattr(value, 'read', None)
                if reader is not None:
                    filename = getattr(value, 'filename',
                                       getattr(value, 'name', None))
                    content_type = getattr(value, 'content_type', None)
                    if content_type is None:
                        content_type = filename and \
                            mimetypes.guess_type(filename)[0] or \
                            'application/octet-stream'
                    if filename is not None:
                        write('; filename="%s"\r\n' % filename)
                    else:
                        write('\r\n')
                    write('Content-Type: %s\r\n\r\n' % content_type)
                    while 1:
>                       chunk = reader(16384)
E                       ValueError: I/O operation on closed file.

..\venv\lib\site-packages\werkzeug\test.py:96: ValueError
===================== 1 failed, 31 passed in 3.89 seconds =====================