Python의 다른 함수에 의해 호출된 모의 패치 1 함수
강력한 코드를 작성할 때마다 단위 테스트는 필수적입니다. 애플리케이션 로직을 확인하고 코드 요구 사항의 다른 모든 측면을 검사하는 데 도움이 됩니다. 그러나 복잡성 및 외부 종속성과 같은 장애물로 인해 품질 테스트 사례를 작성하기가 어렵습니다.
이 시점에서 Python 모의 라이브러리인 unittest.mock
은 이러한 장애물을 극복하는 데 도움이 됩니다. 오늘은 모의객체와 그 중요성, 파이썬 patch()
를 이용한 다양한 예시 코드에 대해 알아보겠습니다.
모의 객체와 그 중요성
모의 개체는 예를 들어 코드가 예상대로 작동하는지 확인하기 위해 다양한 테스트를 수행하는 테스트 환경 내에서 제어된 방식으로 실제 개체의 동작을 모방합니다. 목 객체를 사용하는 과정을 모킹이라고 합니다. 테스트 품질을 향상시키는 강력한 도구입니다.
자, 요점은, 우리는 왜 조롱을 받아야 합니까? 그것을 사용하는 근본적인 이유는 무엇입니까?
다양한 원인이 Python에서 모의 사용의 중요성을 보여줍니다. 아래에서 살펴보겠습니다.
- 주된 이유 중 하나는 목 객체를 사용하여 코드의 동작을 개선하는 것입니다. 예를 들어 HTTP 요청(클라이언트가 서버에 있는 명명된 호스트에 대한 요청)을 테스트하고 간헐적인 실패를 유발하는지 여부를 확인하는 데 사용할 수 있습니다.
- 실제 HTTP 요청을 모의 개체로 대체할 수 있습니다. 이를 통해 외부 서비스 중단과 예측 가능한 방식으로 완전히 성공적인 응답을 가짜로 만들 수 있습니다.
- 모킹은
if
문과except
블록을 테스트하기 어려울 때도 유용합니다. 모의 개체를 사용하여 코드의 실행 흐름을 제어하여 이러한 영역(if
및제외
)에 도달하고 코드 적용 범위를 개선할 수 있습니다. - Python 모의 개체 사용의 중요성을 높이는 또 다른 이유는 우리 코드에서 해당 개체를 사용할 수 있는 방법을 제대로 이해하고 있기 때문입니다.
파이썬 Patch()
와 그 용도
Python의 모의 개체 라이브러리인 unittest.mock
에는 일시적으로 대상을 모의 개체로 대체하는 patch()
가 있습니다. 여기서 대상은 클래스, 메서드 또는 함수가 될 수 있습니다.
패치를 사용하려면 대상을 식별하고 patch()
함수를 호출하는 방법을 이해해야 합니다.
대상을 인식하려면 대상이 임포트 가능한지 확인한 다음 출처가 아닌 활용되는 대상을 패치합니다. 세 가지 방법으로 patch()
를 호출할 수 있습니다. 클래스/함수의 데코레이터, 컨텍스트 관리자 또는 수동 시작/중지로.
클래스/함수의 데코레이터로 patch()
를 사용하거나 with
문 내부의 컨텍스트 관리자에서 patch()
를 사용하면 대상이 새 개체로 대체됩니다. 두 시나리오 모두 with
문 또는 함수가 존재하면 패치가 실행 취소됩니다.
시작 코드를 생성하고 addition.py
파일에 저장하고 patch()
를 데코레이터, 컨텍스트 관리자 및 수동 시작/중지로 사용하기 위해 가져올 것입니다.
시작 예제 코드(addition.py
파일에 저장됨):
def read_file(filename):
with open(filename) as file:
lines = file.readlines()
return [float(line.strip()) for line in lines]
def calculate_sum(filename):
numbers = read_file(filename)
return sum(numbers)
filename = "./test.txt"
calculate_sum(filename)
test.txt
의 내용:
1
2
3
read_file()
은 filename
을 사용하여 행을 읽고 모든 행을 float 유형으로 변환합니다. 변환된 숫자의 목록을 반환하며 calculate_sum()
함수 내부의 numbers
변수에 저장합니다.
이제 calculate_sum()
은 숫자
목록의 모든 숫자를 합산하여 다음과 같이 출력으로 반환합니다.
출력:
6.0
patch()
를 데코레이터로 사용
patch()
를 데코레이터로 사용하는 방법을 단계별로 살펴보겠습니다. 전체 소스 코드는 모든 단계 끝에 제공됩니다.
-
라이브러리 및 모듈을 가져옵니다.
import unittest from unittest.mock import patch import addition
먼저
unittest
라이브러리를 가져온 다음unittest.mock
모듈에서patch
를 가져옵니다. 그런 다음 시작 코드인추가
를 가져옵니다. -
test_calculate_sum()
메서드를 장식합니다.@patch('addition.read_file') def test_calculate_sum(self, mock_read_file): # ....
다음으로
@patch
데코레이터를 사용하여test_calculate_sum()
테스트 메서드를 장식합니다. 여기서 target은addition
모듈의read_file()
함수입니다.test_calculate_sum()
에는@patch
데코레이터를 사용하기 때문에 추가 매개변수mock_read_file
이 있습니다. 이 추가 매개변수는MagicMock
의 인스턴스입니다(mock_read_file
의 이름을 원하는 대로 바꿀 수 있음).test_calculate_sum()
내부에서patch()
는addition.read_file()
함수를mock_read_file
객체로 대체합니다. -
목록을
mock_read_file.return_value
에 할당합니다.mock_read_file.return_value = [1, 2, 3]
-
calculate_sum()
을 호출하고 테스트합니다.result = addition.calculate_sum("") self.assertEqual(result, 6.0)
이제
calculate_sum()
을 호출하고assertEqual()
을 사용하여 합계가6.0
인지 여부를 테스트할 수 있습니다.여기에서
addition.read_file()
함수 대신mock_read_file
객체가 호출되기 때문에calculate_sum()
함수에filename
을 전달할 수 있습니다. -
다음은 전체 소스 코드입니다.
예제 코드(
test_sum_patch_decorator.py
파일에 저장됨)import unittest from unittest.mock import patch import addition class TestSum(unittest.TestCase): @patch("addition.read_file") def test_calculate_sum(self, mock_read_file): mock_read_file.return_value = [1, 2, 3] result = addition.calculate_sum("") self.assertEqual(result, 6.0)
테스트 실행:
python -m unittest test_sum_patch_decorator -v
위의 명령을 사용하여 테스트를 실행하여 다음 출력을 얻습니다.
출력:
test_calculate_sum (test_sum_patch_decorator.TestSum) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.001s OK
patch()
를 컨텍스트 관리자로 사용
예제 코드(test_sum_patch_context_manager.py
파일에 저장됨):
import unittest
from unittest.mock import patch
import addition
class TestSum(unittest.TestCase):
def test_calculate_sum(self):
with patch("addition.read_file") as mock_read_file:
mock_read_file.return_value = [1, 2, 3]
result = addition.calculate_sum("")
self.assertEqual(result, 6)
이 코드는 여기에서 논의된 몇 가지 차이점을 제외하고 patch()
를 데코레이터로 사용한 마지막 코드 예제와 유사합니다.
이제 @patch('addition.read_file')
코드 라인이 없지만 test_calculate_sum()
은 self
매개변수(기본 매개변수)만 사용합니다.
with patch('addition.read_file') as mock_read_file
은 컨텍스트 관리자에서 mock_read_file
객체를 사용하는 addition.read_file()
패치를 의미합니다.
간단히 말해서 patch()
가 addition.read_file()
을 with
블록 내에서 mock_read_file
개체로 대체한다고 말할 수 있습니다.
이제 아래 명령을 사용하여 테스트를 실행하십시오.
python -m unittest test_sum_patch_context_manager -v
출력:
test_calculate_sum (test_sum_patch_context_manager.TestSum) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
patch()
를 사용하여 수동으로 시작/중지
예제 코드(test_sum_patch_manually.py
파일에 저장됨):
import unittest
from unittest.mock import patch
import addition
class TestSum(unittest.TestCase):
def test_calculate_sum(self):
patcher = patch("addition.read_file")
mock_read_file = patcher.start()
mock_read_file.return_value = [1, 2, 3]
result = addition.calculate_sum("")
self.assertEqual(result, 6.0)
patcher.stop()
이 코드 펜스는 앞의 두 코드 예제와 동일하지만 여기서는 patch()
를 수동으로 사용합니다. 어떻게? 아래에서 이해합시다.
먼저 필요한 라이브러리를 가져옵니다. test_calculate_sum()
내부에서 patch()
를 호출하여 addition
모듈의 대상 read_file()
기능으로 패치를 시작합니다.
그런 다음 read_file()
함수에 대한 하나의 모의 개체를 만듭니다. 그런 다음 mock_read_file.return_value
에 숫자 목록을 할당하고 calculate_sum()
을 호출하고 결과를 테스트합니다assertEqual()
사용.
마지막으로 patcher
개체의 stop()
메서드를 호출하여 패치를 중지합니다. 이제 다음 명령을 실행하여 테스트하십시오.
python -m unittest test_sum_patch_manually -v
출력:
test_calculate_sum (test_sum_patch_manually.TestSum) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK