# 使用pytest-mock時如何控制mock object何時結束 ###### tags: `unit-test` `pytest` 在使用pytest-mock的mocker fixture時 所有mock假造的物件都會在當個測試結束時被關掉,所以不會影響其他測試 但如果發生了以下的事情 ```python= def test_function(mocker): mock1 = mocker.patch('module1.function1') # Do some test using mock1 to generate something you want # Stop mock1 from being in effect mocker.stop(mock1) # Do some other test without mock1 ``` 在很不單元的測試內(比如撰寫legacy code的測試),這種狀況常會發生 在同一個測試function內,有時候需要同一個function有時是被mock的,有時是維持正常狀況運作的 這時候stop就會派上用場 只要在測試內使用mocker.stop(),就可以在想要的時機點把對應的MagicMock物件關掉 會讓複雜的測試好寫不少 ## 與unittest.mock的不同 由於文件的缺乏,pytest-mock的doc會叫你去看unittest.mock的文件 ```python= patcher = patch('package.module.ClassName') from package import module original = module.ClassName new_mock = patcher.start() assert module.ClassName is not original assert module.ClassName is new_mock patcher.stop() assert module.ClassName is original assert module.ClassName is not new_mock ``` 但是使用mocker.stop時不能直接把patch完後的MagicMock物件照著上面的作法stop mocker的source code是這樣 ```python= def stop(self, mock: unittest.mock.MagicMock) -> None: """ Stops a previous patch or spy call by passing the ``MagicMock`` object returned by it. """ for index, (p, m) in enumerate(self._patches_and_mocks): if mock is m: p.stop() del self._patches_and_mocks[index] break else: raise ValueError("This mock object is not registered") ``` 所以使用上就會像上個小節一樣,把patch完或spy完的MagicMock物件丟進去就可以了 ```python= mock1 = mocker.patch('module1.function1') mocker.stop(mock1) ``` ## stopall mocker.stopall()可以把所有還在運作的mock都砍掉,也可以達成類似的效果 ## 結語 ~~文件真是個好東西呢~~ 本篇隨筆可以讓各位理解,如果在單個測試過程中想要把某個或全部的mock都關掉時該怎麼做 而當然,各位可能會覺得:測試是不是不該這樣寫 畢竟,mock的東西開開關關,會讓測試本身就難以理解,也更有可能在不預期的地方出錯 但在某些狀況下(如前文所提,處理legacy code的測試),mocker.stop()會降低大型的測試撰寫的難度,而在大型測試存在的狀況下,我們才有辦法開始修改測試與主程式。 那麼以上就是有關pytest-mock stop的用法,希望能幫助到正在撰寫測試的各位。