簡體   English   中英

在python中,如何對沒有返回值的函數進行單元測試?

[英]In python, how to do unit test on a function without return value?

我是蟒蛇。 這些天,我正在推動自己對項目中的某些核心模塊進行更完整的單元測試。 由於我們總是使用“assertEqual”、“assertTrue”等方法進行單元測試,因此這些方法都需要被測試函數的返回值,我想知道如何在沒有返回值的情況下對某個函數進行簡單的單元測試。

我想在這里展示一個小例子,如何在 HelloTest 中測試函數 def foo(self, msg)?

class HelloTest(object):
    def foo(self, msg):
        MSG = msg.upper()
        self.bar(MSG)

    def bar(self, MSG):
        print MSG

正如提到的另一個答案,您可以使用 Python 模擬庫對函數/方法的調用進行斷言

from mock import patch
from my_module import HelloTest
import unittest

class TestFoo(unittest.TestCase):

    @patch('hello.HelloTest.bar')
    def test_foo_case(self, mock_bar):

        ht = HelloTest()

        ht.foo("some string")
        self.assertTrue(mock_bar.called)
        self.assertEqual(mock_bar.call_args[0][0], "SOME STRING")

這修補了 HelloTest 上的bar方法,並將其替換為記錄對其調用的模擬對象。

嘲笑有點像兔子洞。 僅在絕對必要時才這樣做,因為它確實會使您的測試變得脆弱。 例如,您永遠不會注意到模擬對象的 API 更改。

我不太明白為什么每個人都想檢查 foo 是否調用了 bar。

Foo 有一些功能,這個功能需要測試。 如果 foo 使用 bar 來做這件事,我不應該擔心。

期望的結果是在調用foo(msg)之后,將msg.upper()發送到 stdout。

您可以將 stdout 重定向到字符串緩沖區並檢查此字符串緩沖區的內容是否與您期望的相符。

例子:

import sys
import unittest
from io import TextIOWrapper, BytesIO

class TestScript(unittest.TestCase):
    def setUp(self):
        self._old_stdout = sys.stdout
        sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding)

    def _output(self):
        self._stdout.seek(0)
        return self._stdout.read()

    def test_foo(self):
        hello_test = HelloTest()
        hello_test.foo("blub")
        self.assertEqual(self._output(), "BLUB")

    def tearDown(self):
        sys.stdout = self._old_stdout
        self._stdout.close()

您也可以為 stdin 執行此操作(並寫入 stdin 以模擬某些輸入),並且如果您需要完成任何特殊操作,您可以將 TestIOWrapper 子類化,例如允許將非 unicode 文本發送到sys.stdout而不使用sys.stdout.buffer (Python 2 與 Python 3)。 這個 SO answer 中有一個例子。 當您(仍然)僅使用 Python 2 時,使用StringIO可能比使用 io 模塊更好。

在這種特殊情況下,我會模擬打印,然后在我的斷言中使用模擬。

在 Python 中,您將使用Mock 包進行模擬。

感謝@Jordan 的介紹,我對此進行了編碼並認為它​​是 HelloTest.foo 的可行單元測試

from mock import Mock
import unittest


class HelloTestTestCase(unittest.TestCase):
    def setUp(self):
        self.hello_test = HelloTest()

    def tearDown(self):
        pass

    def test_foo(self):
        msg = 'hello'
        expected_bar_arg = 'HELLO'
        self.hello_test.bar = Mock()

        self.hello_test.foo(msg)
        self.hello_test.bar.assert_called_once_with(expected_bar_arg)


if __name__ == '__main__':
    unittest.main()

您的代碼可以如下所示,執行與上述相同的任務:

class HelloTest(object):

    def foo(self, msg):
        self.msg = msg.upper()
        self.bar()

    def bar(self):
        print self.msg

單元測試是:

from hello import HelloTest
import unittest

class TestFoo(unittest.TestCase):
    def test_foo_case(self):
        msg = "test"
        ob = HelloTest()
        ob.foo(msg)
        expected = "TEST"
        self.assertEqual(ob.msg, expected)

if __name__ == '__main__':
    unittest.main(exit=False)

在 Python 3 中,你可以告訴print打印到哪里

打印(*對象,sep='',end='\\n',文件=sys.stdout,flush=False)

所以添加一個可選參數:

def bar(self, MSG, file=sys.stdout):
    print(MSG, file=file)

在正常使用中,它會打印到標准輸出,但對於單元測試,您可以傳遞自己的文件。

在 Python 2 中,它有點混亂,但您可以將 stdout 重定向到 StringIO 緩沖區

import StringIO
import sys

out = StringIO.StringIO()
sys.stdout = out

# run unit tests

sys.stdout = sys.__stdout__

# check the contents of `out`

暫無
暫無

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

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