編寫測試類,Debug代碼。
- 開發中測試與debug代碼是十分重要的一個環節。本章中我們將要使用自動測試來健全我們前幾章創建的Todo應用。
單元測試。
- 像python這樣沒有編譯過程的動態語言,類型錯誤是非常常見的,所以使用單元測試來健壯我們的代碼是非常重要的。
- 編寫測試類時,我們需要有2個目標 。
- 需要擴大測試范圍。盡量包含所有代碼。設計能夠涵蓋所有代碼的測試實例
- 判斷代碼是否正確。
針對第二個目標,我們的測試類代碼實例編寫就會變得比較容易。
添加單元測試
- Odoo中,添加測試類python代碼我們一般會新建一個名為tests的子文件夾。
- 創建 tests/test_wizard.py 文件,添加代碼
from odoo.tests.common import TransactionCase
class TestWizard(TransactionCase):
def setUp(self, *args, **kwargs):
super(TestWizard,self).setUp(*args, **kwargs)
# Add test setup code here...
def test_populate_tasks(self):
"Populate tasks buttons should add 5 tasks"
# Add test code
- Odoo 提供了一些用于測試的python類,TransactionCase 為每個測試提供了一個單獨的事務處理,會在運行完一個測試實例后回到測試之前的狀態。我們也可以使用 SingleTransactionCase 這個基類,它是把所有的測試都一起執行,直到最后一個測試完成才回到初始狀態。這在需要保持測試之后的狀態十分有用。
- setUp() 方法: 這個方法是用來為測試實例準備基礎數據跟存放變量的。我們通常在其中把基礎數據作為類變量存儲以方便每個測試實例使用。
- 測試方法以 test_為前綴。類似
test_populate_tasks()
.這樣就能被自動化測試單元自動獲取到。 - 自動測試過程中,
test_
前綴測試方法會按序被調用。因為我們是基于TransactionCase
這個基類的.所以在每個測試方法完成后都會回滾到測試開始的狀態.在測試過程中,每個測試方法被執行時,Odoo的控制臺會打印出相應的信息. - Odoo的測試類是基于python的
unittest
原生庫的,更多的操作可以直接參考unittest
庫.
編寫一個測試實例
- 我們來擴展我的
test_populate_task()
方法。這個測試方法是用來測試do_populate_tasks()
這個方法的.首先我們要在測試基礎數據中定義2條Todos應用任務記錄.所以測試代碼如下:
def test_populate_tasks(self):
"Populate tasks buttons should add 5 tasks"
self.wizard.do_populate_tasks()
count = len(self.wizard.task_ids)
self.assertEqual(count, 2, 'number wrong')
- 第一行的docstring 是用來描述我們的測試實例的。它會在測試實例被執行時輸出在控制臺上。
- 判斷測試成功與否的關鍵在于斷言方法。這里的assertEqual()方法表示對count的值與2進行判斷。具體用法就是基于python的unittest庫的。里面所有的斷言方法可以去查看python api。
添加一個新的測試實例,測試 do_mass_update() 方法。
def test_mass_change(self):
'Mass change deadline'
self.wizard.do_populate_tasks()
self.wizard.new_deadline = self.todo1.date_deadline
self.wizard.do_mass_update()
self.assertEqual(
self.todo1.date_deadline,
self.todo2.date_deadline,
)
這個測試實例很好理解,首先調用了do_populate_tasks()
方法,把我們的兩個task記錄獲取到向導表單中,然后把第一個任務的截止日期作為新的截止日期,執行do_mass_update()
方法.最后進行斷言任務1跟任務2的截止日期肯定是相同的.
設置測試類基礎數據
在使用測試前,應該先把基礎數據準備好,如添加2條用以進行測試的任務記錄.
我們在使用測試類實例時,為了能測試不同的用戶權限,可以使用sudo()
這個方法來改變當前的用戶環境.
class TestWizard(TransactionCase):
def setUp(self, *args, **kwargs):
super(TestWizard,self).setUp(*args, **kwargs)
self.env['todo.task'].search([('is_done','=',False)]).write({'is_done':True})
# Demo user will be used to run tests
demo_user = self.env.ref('base.user_demo')
# Create two Todo tasks to use in tests
t0 = date.today()
Todo = self.env['todo.task'].sudo(demo_user)
self.todo1 = Todo.create({
'name': 'Todo1',
'date_deadline': fields.Date.to_string(t0),
})
self.todo2 = Todo.create({
'name': 'Todo2',
})
# Create Wizard instance to use in tests
Wizard = self.env['todo.wizard'].sudo(demo_user)
self.wizard = Wizard.create({})
為了測試我們的向導邏輯,我們首先定義了2條新的Todo tasks 記錄, 然后指定測試用戶為Demo
用戶. 最后創建向導實例來作為測試的基礎數據.
測試異常
很多時候我們需要測試代碼中是否會有異常拋出.
在下面的例子中,測試方法test_count()
使用了with self.assertRaises()
來測試向導表單中的方法do_count_tasks()
是否能拋出Warning異常.
from odoo.exceptions import Warning
def test_count(self):
"Test count button"
with self.assertRaises(Warning) as e:
self.wizard.do_count_tasks()
self.assertIn(' 2 ', str(e.exception))
上面的代碼中,我們還對拋出異常后的異常信息進行斷言,判斷2
是否包含在異常信息中。因為根據do_count_tasks()
這個方法,會把任務的總數作為警告信息拋出。我們正好可以使用2
來判斷是否把總的任務個數傳遞出來。
運行測試類
使用命令
./odoo-bin -d todo --test-enable -i todo_wizard --stop-after-init --addons-path="..."
來執行我們已經編寫好的測試實例.
關于YAML 測試
這種測試方式在Odoo10中已經基本不使用了 .
開發工具
- 一些常用的服務端開發選項.
我們在第一章的時候就介紹過,在開啟Odoo服務時,添加選項--dev
能夠開啟Odoo自帶的開發者模式,其中有一些功能對開發來說是十分有效率的.- 當應用模塊中發現異常時,會進入到debug模式
- 自動重載Python代碼.省去手動重啟.
- 能夠直接讀取xml文件的變動來改變視圖.不用自己去手動更新.
默認情況下, 在dev開啟后會使用python自帶的調試器 pdb. Odoo同時支持 ipdb 跟 pudb.
調試代碼
- 調試代碼在開發過程中十分重要.我們經常會采用打斷點來運行我們的程序,在一步步的程序運行過程中分析各個變量,找到我們代碼bug所在.
Python 調試器
- 在Odoo中我們使用 pdb 作為調試器來對代碼進行調試。
- 調試代碼過程關鍵在于插入斷點,在認為可能出現bug的代碼前插入斷點。
import pdb; pdb.set_trace()
然后重啟服務,Odoo程序就會在斷點的地方停止,出現pdb 交互界面,等待你的進一步輸入。注意,手動插入pdb斷點,不需要設置--dev
選項也可以進行。如果設置了--dev
選項,會在應用報錯后自動進入pdb交互模式。
pdb調試常用指令:
- h(help) :顯示pdb命令幫助
- p(print) :輸出
- pp(pretty print) : 更加美觀的輸出,一般在打印字典或者列表結構時使用.
- l(list) : 展示當前行代碼段
- n(next) : 執行下一行
- s(step) : 進入當前指令,通常是進入方法體內
- c(continue) : 繼續執行,直到下一個斷點
- u(up) : 在call stack中上移一層
- d(down) : 在call stack 中下移一層
一個簡單的調試會話事例
- 我們首先在
do_populate_tasks
這個向導方法中插入斷點。
def do_populate_tasks(self):
self.ensure_one()
import pdb; pdb.set_trace()
Task = self.env['todo.task']
....
重啟服務端,打開To-do Tasks Wizard 表單,點擊Get All 按鈕,然后返回命令行窗口,會看到服務端進入了pdb調試交互界面。
開頭的第一行表明了目前處于Python文件中的路徑跟目前執行的代碼的行號。第二行展示了Odoo將要執行的命令。
- 在調試會話中,服務日志記錄會被不停的打印,雖然不會妨礙調試,但是大段的日志記錄打印出來也很煩,我們可以在啟動服務時添加參數
--log-handler=werkzeug: CRITICAL
來避免日志的打印?;蛘哌€可以直接把日志等級調低使用--log-level=warn
選項
可選的python調試工具
除了Odoo默認提供的pdb調試工具,還有像ipdb
,pudb
這樣的更加先進的調試工具
- ipdb: 添加了語法高亮
- 安裝:
sudo pip install ipdb
- 使用: `import ipdb; ipdb.set_trace()
- 安裝:
- pudb: 使用了圖形化界面讓變量值,當前棧信息更好的顯示出來。

安裝:
sudo apt-get install python-pudb
或者
sudo pip install pubd
使用方法也類似:
import pudb; pudb.set_trace()