使用 mock 測試 python 應用

oyanglulu發表於2014-05-15

最近在用 nosetestsmock1 為 bottle 應用測試, 發現幾個使用nosetests 要注意的 地方:

1 patch method of module

patch 一個匯入 module 的 method, 因為 method 已經被匯入到目標檔案, 因此必須 要 patch 目標檔案的 該方法, 而不是原 module.

# wsgi.py
from db import get_db

def insert_something():
    get_db().insert(something)

# test.py
import wsgi

# @patch('db.get_db') # this won't work
@patch('wsgi.get_db') # should patch wsgi
def test_insert(mock_get_db):
    mock_get_db.return_value = Database()
    ...

2 patch decorator

Scenario : bottle 的 views 是用 decorator 來定義的, 也就是說當我們測試 bottle 中 得 action 時其實這個 action 是被 views 包住的. return 的 dict 會被 bottle 的 views 函式 render 成 html 並返回. 而我們並不需要測試 views 返回的 html, 而只需要測試 return 的 dict 是否正確.

這種情況太適合 mock 的 patch 來幹了.

  1. patch bottle.view
  2. 讓其返回一個神馬都沒幹的函式
  3. 啟動這個patch
  4. 匯入需要測試的應用程式碼

注意 這裡的patch必須在 import 你的 application 之前, 比如我的應用是 wsgi.py

完整過程是這樣的

from mock import patch, Mock
import bottle
v = patch("bottle.view",return_value = lambda x : x)
v.start() ** 使用 local config 來存放你的密匙
import wsgi

3 mock module that may not exist

Scenario: 一些帶有密匙的配置檔案(比如 google api key 之類的)並不希望被上傳到 github public 上, 但是測試又需要引入這些檔案.(後來我發現了更 elegant 的解決方式 使用 local config 來存放你的密匙). 假設現在含密匙的配置檔案叫 config.py, 目標檔案是 wsgi.py

這樣的話用 patch 不會起任何作用, 因為在你 patch 的時候, 必須要首先引 入目標檔案, 因此目標檔案中的import config 會先拋異常.

好吧, 現在我們必須在引入 wsgi 之前就 patch config. 我們將直接讓系統的 config 模組等於 mock 的 config. 這樣再引入 wsgi 就沒有任何問題了.

import sys
config_mock = Mock(spec=['config'])
config_mock.__name__ = 'config'
sys.modules['config'] = config_mock

4 使用 travis ci 持續整合

持續整合最簡單的配置當然是 Travis CI. 只需要 增加配置檔案 .travis.yml, 只需要定義4個東西. 下面是我的 .travis.yml

language: python
python:
  - "2.7" # version

install: "pip install -r requirements.txt --use-mirrors"  # requirements

script: nosetests  # test or build command

5 使用 local config 來存放你的密匙

這種方式源於 django. 突然想到 django 的 settings.py 最後幾行

try:
    from local_settings import *
except:
    pass

意思是從 local_settings 裡的所有設定匯入, 因此可以把真的帶密匙的真是配置 寫到 local_settings 中.

Footnotes:

1

: mock is now part of the python 3 now. Whee…..

[](http://oyanglul.us/#/gist/9866674/How to use Mock testing Python)

相關文章