Python Language
단위 테스트
수색…
비고
Python을위한 몇 가지 단위 테스트 도구가 있습니다. 이 문서에서는 기본 unittest
모듈에 대해 설명합니다. 다른 테스트 도구에는 py.test
와 nosetests
있습니다. 테스트에 관한 이 파이썬 문서 는 이러한 도구 중 몇 가지를 깊이 비교하지 않고 비교합니다.
예외 테스트
예를 들어 잘못된 입력이 주어지면 프로그램에서 오류가 발생합니다. 이 때문에 실제 잘못된 입력이 주어지면 오류가 발생하는지 확인해야합니다. 따라서 정확한 예외를 확인해야합니다.이 예제에서는 다음 예외를 사용합니다.
class WrongInputException(Exception):
pass
이 예외는 잘못된 입력이 주어진 경우에 발생합니다. 다음과 같은 상황에서 우리는 숫자를 텍스트 입력으로 항상 기대합니다.
def convert2number(random_input):
try:
my_input = int(random_input)
except ValueError:
raise WrongInputException("Expected an integer!")
return my_input
예외가 발생했는지 여부를 확인하기 위해 assertRaises
를 사용하여 해당 예외를 확인합니다. assertRaises
는 두 가지 방법으로 사용할 수 있습니다.
- 일반 함수 호출 사용. 첫 번째 인수는 예외 유형을, 두 번째는 호출 가능 (대개 함수)을 취하고 나머지 인수는이 호출 가능 함수에 전달됩니다.
-
with
절을 사용하여 예외 유형 만 함수에 제공합니다. 이것은 더 많은 코드가 실행될 수 있다는 장점이 있지만 여러 함수가 문제가 될 수있는 동일한 예외를 사용할 수 있으므로주의해서 사용해야합니다. 예 : self.assertRaises (WrongInputException) : convert2number ( "숫자가 아님")
이것은 먼저 다음 테스트 케이스에서 구현되었습니다.
import unittest
class ExceptionTestCase(unittest.TestCase):
def test_wrong_input_string(self):
self.assertRaises(WrongInputException, convert2number, "not a number")
def test_correct_input(self):
try:
result = convert2number("56")
self.assertIsInstance(result, int)
except WrongInputException:
self.fail()
또한 던져서는 안되는 예외를 검사 할 필요가있을 수 있습니다. 그러나 예외가 발생하면 테스트가 자동으로 실패하므로 전혀 필요하지 않을 수 있습니다. 옵션을 보여 주기만하면 두 번째 테스트 메소드는 던져지지 않는 예외를 검사 할 수있는 방법에 대한 사례를 보여줍니다. 기본적으로 이것은 예외를 포착하고 fail
메소드를 사용하여 테스트를 fail
합니다.
unittest.mock.create_autospec 함수 조롱
함수를 조롱하는 한 가지 방법은 create_autospec
함수를 사용하는 것입니다.이 함수는 객체를 스펙에 따라 조롱합니다. 함수를 사용하여 함수를 적절하게 호출 할 수 있습니다.
custom_math.py
에서 함수를 multiply
:
def multiply(a, b):
return a * b
process_math.py
에서 함수 multiples_of
:
from custom_math import multiply
def multiples_of(integer, *args, num_multiples=0, **kwargs):
"""
:rtype: list
"""
multiples = []
for x in range(1, num_multiples + 1):
"""
Passing in args and kwargs here will only raise TypeError if values were
passed to multiples_of function, otherwise they are ignored. This way we can
test that multiples_of is used correctly. This is here for an illustration
of how create_autospec works. Not recommended for production code.
"""
multiple = multiply(integer,x, *args, **kwargs)
multiples.append(multiple)
return multiples
우리는 multiply
을 조롱함으로써 multiples_of
만 테스트 multiples_of
수 있습니다. 아래 예제는 Python 표준 라이브러리 unittest를 사용하지만 pytest 나 nose와 같은 다른 테스트 프레임 워크와 함께 사용할 수 있습니다.
from unittest.mock import create_autospec
import unittest
# we import the entire module so we can mock out multiply
import custom_math
custom_math.multiply = create_autospec(custom_math.multiply)
from process_math import multiples_of
class TestCustomMath(unittest.TestCase):
def test_multiples_of(self):
multiples = multiples_of(3, num_multiples=1)
custom_math.multiply.assert_called_with(3, 1)
def test_multiples_of_with_bad_inputs(self):
with self.assertRaises(TypeError) as e:
multiples_of(1, "extra arg", num_multiples=1) # this should raise a TypeError
unittest 내의 테스트 설치 및 해체
때로는 각 테스트가 실행될 컨텍스트를 준비하려고합니다. setUp
메소드는 클래스의 각 테스트 이전에 실행됩니다. tearDown
은 모든 테스트가 끝날 때마다 실행됩니다. 이 메소드는 선택 사항입니다. TestCases는 협동 적 다중 상속에서 자주 사용되므로 기본 클래스의 setUp
및 tearDown
메서드도 호출되도록이 메서드에서 항상 super
를주의해야합니다. TestCase
의 기본 구현은 빈 setUp
및 tearDown
메서드를 제공하므로 예외를 발생시키지 않고 호출 할 수 있습니다.
import unittest
class SomeTest(unittest.TestCase):
def setUp(self):
super(SomeTest, self).setUp()
self.mock_data = [1,2,3,4,5]
def test(self):
self.assertEqual(len(self.mock_data), 5)
def tearDown(self):
super(SomeTest, self).tearDown()
self.mock_data = []
if __name__ == '__main__':
unittest.main()
addCleanup
+에는 테스트가 실행 된 후에 호출 할 함수를 등록하는 addCleanup
메소드가 있습니다. setUp
성공할 경우에만 호출되는 tearDown
과 달리 addCleanup
을 통해 등록 된 함수는 setUp
에서 처리되지 않은 예외가 발생한 경우에도 호출됩니다. 구체적인 예로서,이 방법은 테스트가 실행되는 동안 등록 된 다양한 모의 객체를 제거하는 것으로 자주 볼 수 있습니다.
import unittest
import some_module
class SomeOtherTest(unittest.TestCase):
def setUp(self):
super(SomeOtherTest, self).setUp()
# Replace `some_module.method` with a `mock.Mock`
my_patch = mock.patch.object(some_module, 'method')
my_patch.start()
# When the test finishes running, put the original method back.
self.addCleanup(my_patch.stop)
이 방법으로 정리를 등록하는 또 다른 이점은 프로그래머가 설치 코드 옆에 정리 코드를 넣을 수 있고 하위 분류자가 tearDown
에서 super
를 호출하는 것을 잊어 버리는 경우 사용자를 보호 할 수 있다는 것입니다.
예외에 대한 주장
함수가 두 가지 방법을 통해 내장 된 unittest를 사용하여 예외를 throw하는지 테스트 할 수 있습니다.
def division_function(dividend, divisor):
return dividend / divisor
class MyTestCase(unittest.TestCase):
def test_using_context_manager(self):
with self.assertRaises(ZeroDivisionError):
x = division_function(1, 0)
그러면 컨텍스트 관리자 내부에서 코드가 실행되고 성공하면 예외가 발생하지 않으므로 테스트가 실패합니다. 코드에서 올바른 유형의 예외가 발생하면 테스트가 계속됩니다.
추가 어설 션을 실행하려면 발생 된 예외의 내용을 가져올 수도 있습니다.
class MyTestCase(unittest.TestCase):
def test_using_context_manager(self):
with self.assertRaises(ZeroDivisionError) as ex:
x = division_function(1, 0)
self.assertEqual(ex.message, 'integer division or modulo by zero')
호출 가능한 함수를 제공함으로써
def division_function(dividend, divisor):
"""
Dividing two numbers.
:type dividend: int
:type divisor: int
:raises: ZeroDivisionError if divisor is zero (0).
:rtype: int
"""
return dividend / divisor
class MyTestCase(unittest.TestCase):
def test_passing_function(self):
self.assertRaises(ZeroDivisionError, division_function, 1, 0)
확인해야 할 예외는 첫 번째 매개 변수 여야하며 호출 가능한 함수는 두 번째 매개 변수로 전달되어야합니다. 지정된 다른 매개 변수는 호출중인 함수에 직접 전달되어 예외를 트리거하는 매개 변수를 지정할 수 있습니다.
Unittest 내의 어설 션 선택
파이썬에는 assert
문 이 있지만 Python 단원 테스팅 프레임 워크는 테스트에 특화된 더 나은 어설 션을 제공합니다. 즉, 실패에 대해 더 많은 정보를 제공하며 실행의 디버그 모드에 의존하지 않습니다.
아마도 가장 간단한 단언은 assertTrue
. 다음과 같이 사용할 수 있습니다 :
import unittest
class SimplisticTest(unittest.TestCase):
def test_basic(self):
self.assertTrue(1 + 1 == 2)
이것은 정상적으로 돌아가지만 위의 행을 다음과 같이 바꿉니다.
self.assertTrue(1 + 1 == 3)
실패 할 것이다.
assertTrue
어설 션은 테스트 된 것이 부울 조건으로 캐스팅 될 수 있기 때문에 가장 일반적인 어설 션일 가능성이 높지만 종종 더 나은 대안이 있습니다. 위와 같이 평등을 테스트 할 때,
self.assertEqual(1 + 1, 3)
전자가 실패하면 메시지는 다음과 같습니다.
======================================================================
FAIL: test (__main__.TruthTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "stuff.py", line 6, in test
self.assertTrue(1 + 1 == 3)
AssertionError: False is not true
그러나 후자가 실패하면 메시지는
======================================================================
FAIL: test (__main__.TruthTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "stuff.py", line 6, in test
self.assertEqual(1 + 1, 3)
AssertionError: 2 != 3
더 많은 정보를 제공합니다 (실제로는 왼쪽 측면의 결과를 평가했습니다).
어설 션리스트 는 표준 문서에서 찾을 수 있습니다. 일반적으로 조건에 가장 적합한 조건 인 어설 션을 선택하는 것이 좋습니다. 이와 같이 그 주장 들어 위 1 + 1 == 2
는 사용하는 것이 좋다 assertEqual
보다 assertTrue
. 비슷하게, a is None
이라는 것을 주장 할 a is None
, assertEqual
보다 assertIsNone
을 사용하는 것이 낫다.
단정문에는 부정적인 형식이 있음에 유의하십시오. 따라서 assertEqual
에는 음수의 대응 assertNotEqual
가 있고 assertIsNone
에는 음수의 대응 assertIsNotNone
있습니다. 다시 한 번 적절한 경우 음수 카운터를 사용하면 더 명확한 오류 메시지가 표시됩니다.
pytest를 이용한 단위 테스트
pytest 설치하기 :
pip install pytest
테스트 준비하기 :
mkdir tests
touch tests/test_docker.py
docker_something/helpers.py
에서 테스트 할 함수 :
from subprocess import Popen, PIPE
# this Popen is monkeypatched with the fixture `all_popens`
def copy_file_to_docker(src, dest):
try:
result = Popen(['docker','cp', src, 'something_cont:{}'.format(dest)], stdout=PIPE, stderr=PIPE)
err = result.stderr.read()
if err:
raise Exception(err)
except Exception as e:
print(e)
return result
def docker_exec_something(something_file_string):
fl = Popen(["docker", "exec", "-i", "something_cont", "something"], stdin=PIPE, stdout=PIPE, stderr=PIPE)
fl.stdin.write(something_file_string)
fl.stdin.close()
err = fl.stderr.read()
fl.stderr.close()
if err:
print(err)
exit()
result = fl.stdout.read()
print(result)
테스트는 test_docker.py
가져 test_docker.py
.
import os
from tempfile import NamedTemporaryFile
import pytest
from subprocess import Popen, PIPE
from docker_something import helpers
copy_file_to_docker = helpers.copy_file_to_docker
docker_exec_something = helpers.docker_exec_something
test_docker.py
에서 객체와 같은 파일 조롱 :
class MockBytes():
'''Used to collect bytes
'''
all_read = []
all_write = []
all_close = []
def read(self, *args, **kwargs):
# print('read', args, kwargs, dir(self))
self.all_read.append((self, args, kwargs))
def write(self, *args, **kwargs):
# print('wrote', args, kwargs)
self.all_write.append((self, args, kwargs))
def close(self, *args, **kwargs):
# print('closed', self, args, kwargs)
self.all_close.append((self, args, kwargs))
def get_all_mock_bytes(self):
return self.all_read, self.all_write, self.all_close
test_docker.py에서 test_docker.py
Monkey 패칭하기 :
@pytest.fixture
def all_popens(monkeypatch):
'''This fixture overrides / mocks the builtin Popen
and replaces stdin, stdout, stderr with a MockBytes object
note: monkeypatch is magically imported
'''
all_popens = []
class MockPopen(object):
def __init__(self, args, stdout=None, stdin=None, stderr=None):
all_popens.append(self)
self.args = args
self.byte_collection = MockBytes()
self.stdin = self.byte_collection
self.stdout = self.byte_collection
self.stderr = self.byte_collection
pass
monkeypatch.setattr(helpers, 'Popen', MockPopen)
return all_popens
예제 테스트는 test_docker.py
파일의 접두사 test_
로 시작해야합니다.
def test_docker_install():
p = Popen(['which', 'docker'], stdout=PIPE, stderr=PIPE)
result = p.stdout.read()
assert 'bin/docker' in result
def test_copy_file_to_docker(all_popens):
result = copy_file_to_docker('asdf', 'asdf')
collected_popen = all_popens.pop()
mock_read, mock_write, mock_close = collected_popen.byte_collection.get_all_mock_bytes()
assert mock_read
assert result.args == ['docker', 'cp', 'asdf', 'something_cont:asdf']
def test_docker_exec_something(all_popens):
docker_exec_something(something_file_string)
collected_popen = all_popens.pop()
mock_read, mock_write, mock_close = collected_popen.byte_collection.get_all_mock_bytes()
assert len(mock_read) == 3
something_template_stdin = mock_write[0][1][0]
these = [os.environ['USER'], os.environ['password_prod'], 'table_name_here', 'test_vdm', 'col_a', 'col_b', '/tmp/test.tsv']
assert all([x in something_template_stdin for x in these])
한 번에 하나씩 테스트 실행 :
py.test -k test_docker_install tests
py.test -k test_copy_file_to_docker tests
py.test -k test_docker_exec_something tests
tests
폴더의 모든 테스트 실행 :
py.test -k test_ tests