Python Mock的入門

cncde 9年前發布 | 13K 次閱讀 Python開發 Python Mock

Mock是什么

Mock這個詞在英語中有模擬的這個意思,因此我們可以猜測出這個庫的主要功能是模擬一些東西。準確的說,Mock是Python中一個用于支持的測試的庫,它的主要功能是使用mock對象替代掉指定的Python對象,以達到模擬對象的行為。簡單的說,mock庫用于如下的場景:

假設你開發的項目叫a,里面包含了一個模塊b,模塊b中的一個函數c(也就是a.b.c)在工作的時候需要調用發送請求給特定的服務器來得到一個JSON返回值,然后根據這個返回值來做處理。如果要為a.b.c函數寫一個單元測試,該如何做?

</blockquote>

一個簡單的辦法是搭建一個測試的服務器,在單元測試的時候,讓a.b.c函數和這個測試服務器交互。但是這種做法有兩個問題:

  1. 測試服務器可能很不好搭建,或者搭建效率很低。
  2. 你搭建的測試服務器可能無法返回所有可能的值,或者需要大量的工作才能達到這個目的。
  3. </ol>

    那么如何在沒有測試服務器的情況下進行上面這種情況的單元測試呢?Mock模塊就是答案。上面已經說過了,mock模塊可以替換Python對象。我們假設a.b.c的代碼如下:

    import requests

    def c(url): resp = requests.get(url)

    # further process with resp</pre> <p>如果利用mock模塊,那么就可以達到這樣的效果:<strong>使用一個mock對象替換掉上面的requests.get函數,然后執行函數c時,c調用requests.get的返回值就能夠由我們的mock對象來決定,而不需要服務器的參與</strong>。簡單的說,就是我們用一個mock對象替換掉c函數和服務器交互的過程。你一定很好奇這個功能是如何實現的,這個是mock模塊內部的實現機制,不在本文的討論范圍。本文主要討論如何用mock模塊來解決上面提到的這種單元測試場景。 </p>
    

    Mock的安裝和導入

    在Python 3.3以前的版本中,需要另外安裝mock模塊,可以使用pip命令來安裝:

    $ sudo pip install mock

    然后在代碼中就可以直接import進來:

    import mock

    從Python 3.3開始,mock模塊已經被合并到標準庫中,被命名為unittest.mock,可以直接import進來使用:

    from unittest import mock

    Mock對象

    基本用法

    Mock對象是mock模塊中最重要的概念。Mock對象就是mock模塊中的一個類的實例,這個類的實例可以用來替換其他的Python對象,來達到模擬的效果。Mock類的定義如下:

    class Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, **kwargs)

    這里給出這個定義只是要說明下Mock對象其實就是個Python類而已,當然,它內部的實現是很巧妙的,有興趣的可以去看mock模塊的代碼。

    Mock對象的一般用法是這樣的:

    1. 找到你要替換的對象,這個對象可以是一個類,或者是一個函數,或者是一個類實例。
    2. 然后實例化Mock類得到一個mock對象,并且設置這個mock對象的行為,比如被調用的時候返回什么值,被訪問成員的時候返回什么值等。
    3. 使用這個mock對象替換掉我們想替換的對象,也就是步驟1中確定的對象。
    4. 之后就可以開始寫測試代碼,這個時候我們可以保證我們替換掉的對象在測試用例執行的過程中行為和我們預設的一樣。
    5. </ol>

      舉個例子來說:我們有一個簡單的客戶端實現,用來訪問一個URL,當訪問正常時,需要返回狀態碼200,不正常時,需要返回狀態碼404。首先,我們的客戶端代碼實現如下:

      #!/usr/bin/env python

      -- coding: utf-8 --

      import requests

      def send_request(url): r = requests.get(url) return r.status_code

      def visit_ustack(): return send_request('外部模塊調用visit_ustack()來訪問UnitedStack的官網。下面我們使用mock對象在單元測試中分別測試訪問正常和訪問不正常的情況。

      #!/usr/bin/env python

      -- coding: utf-8 --

      import unittest

      import mock

      import client

      class TestClient(unittest.TestCase):

      def test_success_request(self):
          success_send = mock.Mock(return_value='200')
          client.send_request = success_send
          self.assertEqual(client.visit_ustack(), '200')
      
      def test_fail_request(self):
          fail_send = mock.Mock(return_value='404')
          client.send_request = fail_send
          self.assertEqual(client.visit_ustack(), '404')</pre> <ol>
      

    6. 找到要替換的對象:我們需要測試的是visit_ustack這個函數,那么我們需要替換掉send_request這個函數。
    7. 實例化Mock類得到一個mock對象,并且設置這個mock對象的行為。在成功測試中,我們設置mock對象的返回值為字符串“200”,在失敗測試中,我們設置mock對象的返回值為字符串"404"。
    8. 使用這個mock對象替換掉我們想替換的對象。我們替換掉了client.send_request
    9. 寫測試代碼。我們調用client.visit_ustack(),并且期望它的返回值和我們預設的一樣。
    10. </ol>

      上面這個就是使用mock對象的基本步驟了。在上面的例子中我們替換了自己寫的模塊的對象,其實也可以替換標準庫和第三方模塊的對象,方法是一樣的:先import進來,然后替換掉指定的對象就可以了。

      稍微高級點的用法

      class Mock的參數

      上面講的是mock對象最基本的用法。下面來看看mock對象的稍微高級點的用法(并不是很高級啊,最完整最高級的直接去看mock的文檔即可,后面給出)。

      先來看看Mock這個類的參數,在上面看到的類定義中,我們知道它有好幾個參數,這里介紹最主要的幾個:

 本文由用戶 cncde 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!