簡體   English   中英

為單元測試創​​建一個類似對象的 gzip 文件

[英]Create a gzip file like object for unit testing

我想測試一個 Python 函數,它讀取一個 gzip 文件並從文件中提取一些東西(使用 pytest)。

import gzip

def my_function(file_path):
    output = []

    with gzip.open(file_path, 'rt') as f:
        for line in f:
            output.append('something from line')

    return output

我可以創建一個 gzip 文件,比如我可以傳遞給my_function對象嗎? 該對象應已定義內容並應與gzip.open()

我知道我可以在夾具中創建一個臨時 gzip 文件,但這取決於文件系統和環境的其他屬性。 從代碼創建類似文件的對象會更便攜。

您可以使用iogzip庫來創建內存文件對象。 例子:

import io, gzip

def inmem():
    stream = io.BytesIO()
    with gzip.open(stream, 'wb') as f:
        f.write(b'spam\neggs\n')
    stream.seek(0)
    return stream

您永遠不應該嘗試在單元測試中測試外部代碼。 只測試你寫的代碼。 如果您正在測試gzip ,則gzip做錯了(他們應該編寫自己的單元測試)。 相反,請執行以下操作:

from unittest import mock

@mock.Mock('gzip', return_value=b'<whatever you expect to be returned from gzip>')
def test_my_function(mock_gzip):
    file_path = 'testpath'
    output = my_function(file_path=file_path)
    mock_gzip.open.assert_called_with(file_path)
    assert output == b'<whatever you expect to be returned from your method>'

這就是你的整個單元測試。 您只想知道gzip.open()已被調用(並且您假設它有效,否則gzip失敗了,這就是他們的問題)並且您從被測試的方法中得到了您期望的結果。 您可以根據期望返回的內容指定gzip返回的內容,但實際上並未在測試中調用該函數。

這有點冗長,但我會做這樣的事情(我假設您將 my_function 保存到名為 patch_one.py 的文件中):

import patch_one  # this is the file with my_function in it
from unittest.mock import patch
from unittest import TestCase


class MyTestCase(TestCase):
    def test_my_function(self):
        # because you used "with open(...) as f", we need a mock context
        class MyContext:
            def __enter__(self, *args, **kwargs):
                return [1, 2]  # note the two items

            def __exit__(self, *args, **kwargs):
                return None

        # in case we want to know the arguments to open()
        open_args = None

        def f(*args, **kwargs):
            def my_open(*args, **kwargs):
                nonlocal open_args
                open_args = args
                return MyContext()

            return my_open

        # patch the gzip.open in our file under test
        with patch('patch_one.gzip.open', new_callable=f):  

            # finally, we can call the function we want to test
            ret_val = patch_one.my_function('not a real file path')

            # note the two items, corresponding to the list in __enter__()
            self.assertListEqual(['something from line', 'something from line'], ret_val)

            # check the arguments, just for fun
            self.assertEqual('rt', open_args[1])

如果你想嘗試更復雜的東西,我建議你閱讀 unittest 模擬文檔,因為你如何導入“patch_one”文件和你傳遞給 patch() 的字符串一樣重要。

肯定有辦法用 Mock 或 MagicMock 做到這一點,但我發現它們有點難以調試,所以我走了很長一段路。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM