В python, как сделать unit test функцию без возвращаемого значения?

Я пионер. В эти дни я заставляю себя сделать более полный unit test на каком-то основном модуле в моем проекте. Поскольку мы всегда делаем unit test с методами "assertEqual", "assertTrue" и т.д., Эти методы требуют требуемого значения от проверяемой функции, мне интересно, как сделать обычную unit test для некоторой функции без возврата значение.

Я хотел бы показать здесь крошечный пример, как проверить функцию def foo (self, msg) в HelloTest?

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

    def bar(self, MSG):
        print MSG

Ответы

Ответ 1

В этом конкретном случае я бы высмеял печать, а затем использовал макет в моем утверждении.

В Python вы будете использовать пакет Mock, чтобы высмеять.

Ответ 2

Как уже упоминалось в другом ответе, вы можете использовать фиктивную библиотеку 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")

Это исправляет метод bar в HelloTest и заменяет его фиктивным объектом, который записывает вызовы против него.

Насмешка - это немного кроличья нора. Делайте это только тогда, когда это абсолютно необходимо, потому что это делает ваши тесты хрупкими. Например, вы никогда не заметите изменения API для макета объекта.

Ответ 3

Я не совсем понимаю, почему каждый хочет проверить эту панель вызовов foo.

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-ответа. Если вы все еще используете Python 2, то использование StringIO может быть лучше, чем использование модуля io.

Ответ 4

ваш код может быть указан ниже, который выполняет ту же задачу, что и выше:

class HelloTest(object):

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

    def bar(self):
        print self.msg

Unit test:

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)

Ответ 5

В Python 3 вы можете сообщить print, где печатать в:

print (* objects, sep = '', end = '\n', file = sys.stdout, flush = False)

Итак, добавьте необязательный аргумент:

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

При нормальном использовании он будет печатать на stdout, но для модульных тестов вы можете передать свой собственный файл.

В 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`

Ответ 6

Благодаря введению @Jordan, я кодирую это и думаю, что это выполнимый unit test для 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()