2013-03-27 101 views
6

我正在使用Django REST框架來處理我正在使用的API。出於幾個原因,我想使用基於類的視圖。但是,我對單元測試有點特別,並且我從不允許單元測試觸及數據庫。注意:我總是使用Carl Meyer在Pycon 2012上演示的「技巧」,在那裏他嘲笑了Cursor包裝。Django基於類的視圖中的嘲諷函數

cursor_wrapper = Mock() 
cursor_wrapper.side_effect = RuntimeError("No touching the database!") 

@patch('django.db.backends.util.CursorWrapper', cursor_wrapper) 
class TestMyCode(TestCase): 

這裏是link如果你有興趣的幻燈片。

我在其中一個視圖中檢查數據庫中的東西的方法。要進行DRY,它將在POST和PUT之間共享。但是,我在單元測試中嘲笑它有問題。這是因爲classmethod as_view創建了一個新的實例和類派發,並返回了派發返回值的「處理函數」。所以,我似乎無法在我的基於類的視圖中獲取共享方法來嘲笑它。

我可以嘲笑基於類的視圖使用的模型,但是我必須打破「幹」的目標,並在POST和PUT中複製代碼。我想我可以重構代碼並將邏輯移到模型上。但是,我不積極,我想這樣做。

如何嘲笑基於類的視圖的共享方法以避免實際觸及數據庫?避免它們?

回答

3

我想你回答了你自己的問題。你用來測試任何Web框架的東西都適用於Django,比如控制和依賴注入的反轉。你可以在Python中保持它非常簡單,所以不要被Spring中存在的東西嚇倒或關閉。

爲什麼不把代碼移出基於類的視圖?如果您出於某種原因在其他地方需要相同的邏輯,您的代碼仍然不會幹。僅僅因爲它是Django並不意味着好的編程原則不適用。

我建議在新的類/ python模塊(如服務(作爲一個概念,而不是Django的服務定義)和其他數據訪問的邏輯抽象)中抽象一些東西。那麼你完全獨立於Django視圖的請求/響應生命週期。 Django和Rails開發人員傾向於將所有邏輯直接放在模型或視圖中。這隻會導致上帝的課程和難以測試的東西。

您也可以通過將您的視圖看作是輕量級抽象來處理其他代碼,並調用其他地方封裝的必要邏輯來處理像編組參數(GET/POST)等事情。國際海事組織,如果你想要可測試的代碼,99%的邏輯應該在Web上下文之外,除非它對於這個過程是絕對關鍵的。這使得它更容易在背景中並行運行。

你應該最終得到的是普通的python模塊和類,它們很容易測試,因爲它們沒有直接依賴HTTP。如果你需要模擬HTTP,你可以簡單地模擬請求對象。你很幸運,因爲python/django的組合很容易將這些東西轉儲出來並作爲簡單的dicts/kwargs來模擬。

我使用基於類的視圖實現的一件事是它們適合與mixin一起使用並強制執行一些約定(返回json,打開圖屬性等),但使用更「高級」的基於類的視圖直接需要模型如DetailView只是使事情不必要地複雜化。這些視圖對管理界面來說很不錯,但對於真正的應用程序來說,幫助不止於此。除非你找到一個好的,無縫的方式來集成緩存層等,否則他們會使測試和謀殺性能變得很難。此時,通常只是從View或TemplateView繼承並完成它。

關於數據庫嘲笑具體來說,創建你的嘲笑,並通過你的業務邏輯。那麼只要它符合一組特定的規則和接口,就不會影響你輸入/輸出的內容。例如,請參閱Mixer之類的內容。您也可以在測試期間簡單地創建/銷燬臨時數據庫。一種方法是爲dev/staging/production/testing等創建單獨的設置模塊,並根據環境動態加載它們。這樣,您可以在運行單元測試時避免損壞工作開發數據庫。當然這更多的是進入一種集成測試的形式,但你也應該做一些這樣的事情。上述解決方案在其他ORM(如Hibernate)中很常見。

與之前相比,您可以在設置中執行類似以下代碼的操作,以使用內存數據庫進行單元測試。不過最終,你還是需要考慮集成測試針對實際數據存儲類型,例如MySQL的

if 'test' in sys.argv: 
    DATABASES['default']['ENGINE'] = 'sqlite3' 

; tldr

  1. 把你的邏輯課外意見納入適當的對象和模塊。

  2. 不要將自己鎖在試圖使各種捆綁的基於類的視圖適用於真實應用程序和每個用例;推出自己的。

  3. 使用通常良好的TDD原則,例如IOC,將必需的參數傳遞給構造函數,鬆散地耦合事物,避免過多的專有狀態要求(特別是HTTP)。

    1. 通過創建標準模擬對象(請參閱#3)並通過類似服務的接口(請參閱#1)避免數據庫依賴性。