관리 메뉴

Leo's Garage

smtm #3 - mock 본문

Study/AutoTradingBot

smtm #3 - mock

LeoBehindK 2023. 1. 15. 11:44
728x90
반응형

mock란 실제 테스트하기에는 비용이 크거나 혹은 테스트할 수 없는 상황에 마치 진짜인 것처럼 동작하게 하는 모조품을 이르는 말이다.

이전 포스트에서 smtm 코드의 테스트 코드 예제를 살펴보면서 REST API의 테스트 코드를 작성하였다.

여기에는 큰 문제가 하나 있는데 REST API는 결국 외부의 서버(업비트)에게 정보를 요청하고 받게 된다는 점이다.

만약에 테스트 환경에서 internet이 안되거나 혹은 업비트의 서버가 불안정해서 정확한 정보를 받을 수 없다면 테스트 자체를 신뢰할 수 없는 상황이 된다.

우리는 테스트 환경을 일정하게 만들 필요가 있다.

실험 시 외부 요인을 통제하지 못한다면 실험 자체가 무의미한 것과 마찬가지이기 때문이다. 

이러한 환경을 통제하기 위해서 우리는 unittest 모듈 내에 mock 객체를 사용하고자 한다. 

https://docs.python.org/ko/3/library/unittest.mock-examples.html

 

unittest.mock — getting started

Using Mock: Mock Patching Methods: Common uses for Mock objects include: Patching methods, Recording method calls on objects. You might want to replace a method on an object to check that it is cal...

docs.python.org

mock 객체에 대한 자세한 설명은 상기 링크에서 확인할 수 있다. 

mock를 사용한 예제 테스트 코드를 살펴보자

    @patch("requests.get")
    def test_initialize_from_server_update_data_correctly_with_empty_data(self, mock_get):
        ex = TddExercise()
        dummy_response = MagicMock()
        mock_get.return_value = dummy_response

        ex.initialize_from_server()
        self.assertEqual(len(ex.data), 0)

    @patch("requests.get")
    def test_initialize_from_server_update_data_correctly(self, mock_get):
        ex = TddExercise()
        dummy_response = MagicMock()
        dummy_response.json.return_value = [{"market": "apple"}, {"market": "banana"}]
        mock_get.return_value = dummy_response

        ex.initialize_from_server()
        self.assertEqual(len(ex.data), 2)
        self.assertEqual(ex.data[0], {"market": "apple"})
        self.assertEqual(ex.data[1], {"market": "banana"})

        mock_get.assert_called_once_with(ex.URL, params=ANY)
        self.assertEqual(mock_get.call_args[1]["params"]["count"], 100)

@patch("request.get") 

이 표현은 데코레이터라고 하는데 위와 같이 표기하면 해당 함수 내에서 requests 모듈 대신 mock 객체를 사용할 수 있게 된다.

 

test_initialize_from_server_update_data_correctly_with_empty_data 함수를 호출해보자.

TddExercise() 객체를 생성하였다. 

dummy_response를 생성하고 해당 값을 mock.get의 return value로 설정한다.

이렇게 하면 해당 함수 내에서 requests get을 호출하면 mock.get의 return value를 반환하게 된다.

ex 객체에서 initialize_from_server를 호출하였고, 그 아래 결과로 데이터가 0임을 확인한다. 

dummy_response 자체가 데이터가 아무것도 없으므로 length가 0임은 자명하다는 걸 알 수 있다. 

 

좀더 복잡한 테스트로는 test_initialize_from_server_update_data_corretly를 살펴보자.

여기서는 dummy_response에 임의의 데이터를 집어넣었다. 

총 2개의 데이터를 집어넣었고, 각 데이터의 위치에 값이 정상적으로 들어와 있는지 확인한다. 

 

다만, 이 방식은 requests.get의 응답을 통제할 수는 있지만, 실제 파라미터들이 제대로 들어갔는지를 확인하지는 않는다.

        mock_get.assert_called_once_with(ex.URL, params=ANY)
        self.assertEqual(mock_get.call_args[1]["params"]["count"], 100)

위의 코드를 살펴보자.

assert_called_once_with은 한 번만 호출되어야 하는데 이 때, ex.URL이 들어가야 한다. 

이는 정해진 URL이 아니라 다른 URL로 호출될 수도 있음을 확인하는 것이다.

구조상 기존의 테스트 코드는 정해진 URL이 아닌 다른 URL이 들어가더라도 호출이 가능했다.

 

call_arg는 객체가 마지막으로 호출된 인자이다. 

즉 count parameter가 100으로 호출되었는지를 확인하는 코드라고 볼 수 있다. 

 

이 리뷰는 아래 책을 바탕으로 작성되었습니다.

http://www.yes24.com/Product/Goods/107635612

 

암호화폐 자동매매 시스템 만들기 with 파이썬 - YES24

이 책은 암호화폐 자동거래 프로그램의 설계부터 개발 후 활용까지 모든 과정을 다루고 있다. 파이썬의 기본적인 문법부터 설명하고 있지만 대부분은 실제 코드를 개발하는 과정과 함께 파이썬

www.yes24.com

 

728x90
반응형

'Study > AutoTradingBot' 카테고리의 다른 글

smtm #2 - TDD 예시  (0) 2023.01.15
smtm #1 - TDD 맛보기  (0) 2023.01.06
Comments