Ответ 1
В этом конкретном случае я бы высмеял печать, а затем использовал макет в моем утверждении.
В Python вы будете использовать пакет Mock, чтобы высмеять.
Я пионер. В эти дни я заставляю себя сделать более полный 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
В этом конкретном случае я бы высмеял печать, а затем использовал макет в моем утверждении.
В Python вы будете использовать пакет Mock, чтобы высмеять.
Как уже упоминалось в другом ответе, вы можете использовать фиктивную библиотеку 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 для макета объекта.
Я не совсем понимаю, почему каждый хочет проверить эту панель вызовов 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.
ваш код может быть указан ниже, который выполняет ту же задачу, что и выше:
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)
В 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`
Благодаря введению @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()