unit test mock up / unittest mock / mock 사용하기 / 파이썬에서 mock 사용하기
Mock module
python 에서 mock 을 사용해 보자.ref. 1 에서 stub/fake 와 mock 을 따로 구분하고 있는데, 사실 전부 가상의 object 라고 할 수 있는데, mock 을 좀 더 기능이 많은 녀석으로 분류하고 있다.
mock 은 method 가 특정 input 을 넣었을 때 특정 output 이 나오는가를 검증하는 것(state verification) 에 더해서, method 가 특정 method 를 몇 번 호출해야 하고 등등의 method 의 동작을 검증하는 것(behavior verification) 도 가능하게 해 주는 것이라고 이야기 한다.
- state verification
- behavior verification
python 3.0 에서는 표준 module 로 포함되어 있지만, python 2.x 버전(2.4 ~2.7) 에서는 따로 download 받아서 사용해야 한다.[ref. 1] download 는 아래 경로에서 받을 수 있다.
python 3.3 에서 부터 unittest mock up 이 standard library 에 추가됐다. 사용법은 어렵지 않다. 아래 글을 참고하자.
유용한 함수들
- MagicMock : mockfunc = MagicMock(return_value=3) 으로 mockfunc() 을 호출하면 무조건 return 값이 3으로 나오게 할 수 있다.
- assert_called_with : assert_called_with 로 함수가 호출될때 어떤 parameter 를 받는지에 대한 test 를 할 수 있다.
- assert_has_calls : 함수가 어떤 parameter 를 가지고 호출이 되는지를 test할 수 있다.
import unittest from unittest.mock import MagicMock from unittest.mock import ANY, call import myproj.FileMailer class TestFileMailer(unittest.TestCase): def setUp(self): self.fmailer = FileMailer(os.path.abspath(__file__)) self.fmailer.sendThruSendMail = MagicMock(return_value=3) # mock self.fmailerMock = Mock() self.fmailerMock.getTargetList = MagicMock(return_value=[{'user_id': 1}]) def test_send(self): htmlMessage = '<html></html>' subj = 'test1' expSubj = "[새로운] 'test1' 의 메일제목." self.fmailer.send( 'ga@gamil.com', info={'title': subj} ) self.fmailer.sendThruSendMail.assert_called_with(ANY, expSubj, 'my-test') def test_send2(self): self.fmailer.send( 'ga@gamil.com', info={'title': subj} ) self.fmailer.sendThruSendMail.assert_has_calls( [call('ga@gamil.com', info={'title': subj}), call('ga@gamil.com', info={'title': subj})]) def test_send3(self): fm = self.fmailerMock() tlist = fm.getTargetList() for t in tlist: self.assertEqueal(1, t.user_id, 'user_id check')
request test
request 를 mock 으로 돌리고, request 로 넘어오는 parameter 를 점검한다.만약 일부 parameter 를 다른 곳에서 test 한다면, 그 parameter 에 대해서 ANY 를 사용하면 된다.
class CumaTask(object): def __init__(self, channel): super().__init__() self.channel = channel def transfer(self, session, pid): infoMgr = InfoManager(session) info = infoMgr.getInfo(pid) ch = self.channel for row in info: res = ch.request(apiName=MyNetApi.TRANSFER, data=row) return class TestMyNet(unittest.TestCase): maxDiff = None def setUp(self): pass def testMyNetTask(self): pid = self.pid ch = MyNetChannel() ch.request = MagicMock(return_value={}) ctask = MyNetTask(ch) ctask.transfer(self.session, pid) ch.request.assert_has_calls([call(apiName=MyNetApi.TRANSFER, data=ANY)])
댓글 없음:
댓글 쓰기