2015-01-07 86 views
2

模擬測試庫是Django的一個主題,我似乎無法包裝我的頭。例如,在下面的代碼中,爲什麼我的單元測試中創建的模擬用戶實例不出現在我在'get_user_ids'方法中查詢的用戶對象中?如果我通過調試調用停止了「get_user_ids」方法中的測試並執行「User.objects.all()」,則User查詢集中沒有任何內容,並且測試失敗。我是不是創建三個模擬User實例來查詢UserProxy的靜態方法?如何將Django模擬實例傳遞給類方法?

我正在使用Django 1.6和Postgres 9.3並使用命令「python manage.py test -s apps.profile.tests.model_tests:TestUserProxy」運行測試。

謝謝!

# apps/profile/models.py 
from django.contrib.auth.models import User 
class UserProxy(User): 
    class Meta: 
     proxy = True 

    @staticmethod 
    def get_user_ids(usernames): 
     debug() 
     user_ids = [] 
     for name in usernames: 
      try: 
       u = User.objects.get(username__exact=name) 
       user_ids.append(u.id) 
      except ObjectDoesNotExist: 
       logger.error("We were unable to find '%s' in a list of usernames." % name) 
     return user_ids 


# apps/profile/tests/model_tests.py 
from django.test import TestCase 
from django.contrib.auth.models import User 
from mock import Mock 
from apps.profile.models import UserProxy 

class TestUserProxy(TestCase): 
    def test_get_user_ids(self): 
     u1 = Mock(spec=User) 
     u1.id = 1 
     u1.username = 'user1' 

     u2 = Mock(spec=User) 
     u2.id = 2 
     u2.username = 'user2' 

     u3 = Mock(spec=User) 
     u3.id = 3 
     u3.username = 'user3' 

     usernames = [u1.username, u2.username, u3.username] 
     expected = [u1.id, u2.id, u3.id] 
     actual = UserProxy.get_user_ids(usernames) 
     self.assertEqual(expected, actual) 
+0

我再次閱讀您的問題,並將您的意見發送給其他職位。你想創建一個真正的用戶而不是嘲笑:你需要通過'User()'創建真實的對象,就像在https://docs.djangoproject.com/en/1.7/intro/tutorial05/#writing-our-first-測試。 'Mock()'是嘲笑而不是包裝。你應該改變你的問題是明確的...兩個誤解了你想要的... –

回答

4

嘲諷是真棒進行測試,並可能導致非常乾淨的測試,但它受到一點從(一)是有點繁瑣讓那些腦袋周圍開始出來的時候,和(b)確實需要一些經常努力設置模擬對象,然後在正確的地方注入/使用。

您爲用戶創建的模擬對象是看起來像Django User模型對象的對象,但它們不是實際的模型對象,因此不會放入數據庫。

爲了讓您的測試正常工作,您有兩種選擇,具體取決於您想要寫什麼類型的測試。

單位測試 - 莫克從數據庫

第一個選項是得到這個工作作爲一個單元測試,即,從數據庫層測試get_user_ids方法在隔離返回的數據。爲此,您需要模擬對User.objects.get(username__exact=name)的調用,以便返回您在測試中創建的三個模擬對象。這將是更正確的方法(因爲最好是單獨測試代碼單元),但是它會涉及更多的工作來設置比下面的替代方案。實現這一

一種方式是用戶查找首先分離出來,到它自己的功能應用/資料/ models.py

def get_users_by_name(name): 
    return User.objects.get(username__exact=name) 

這需要在你的函數被調用,將替換爲get_users_by_name(name)。然後,您可以修改您的測試修補功能,像這樣:

from django.test import TestCase 
from django.contrib.auth.models import User 
from mock import Mock, patch 
from apps.profile.models import UserProxy 

class TestUserProxy(TestCase): 

    @patch('apps.profile.models.get_user_by_name') 
    def test_get_user_ids(self, mock_get_user_by_name): 
     u1 = Mock(spec=User) 
     u1.id = 1 
     u1.username = 'user1' 

     u2 = Mock(spec=User) 
     u2.id = 2 
     u2.username = 'user2' 

     u3 = Mock(spec=User) 
     u3.id = 3 
     u3.username = 'user3' 

     # Here is where we wire up the mocking - we take the patched method to return 
     # users and tell it that, when it is called, it must return the three mock 
     # users you just created. 
     mock_get_user_by_name.return_value = [u1, u2, u3] 

     usernames = [u1.username, u2.username, u3.username] 
     expected = [u1.id, u2.id, u3.id] 
     actual = UserProxy.get_user_ids(usernames) 
     self.assertEqual(expected, actual) 

集成測試 - 創建真正的用戶對象

第二種方法是修改這是一個集成測試,即一個測試這個代碼單元以及與數據庫的交互。這是不太乾淨的,因爲您現在正在將方法的測試暴露給由於其他代碼單元(即與數據庫交互的Django代碼)中的問題而導致失敗的機會。但是,這確實使測試的設置變得更簡單,並且務實可能是適合您的方法。

要做到這一點,只需刪除已創建的模擬並在數據庫中創建實際用戶作爲測試的一部分。

+0

謝謝你的努力。雖然你的第一種方法是有啓發性的,但我不認爲這是我所尋求的解決方案,因爲它的重點是生成模擬用戶來填充用戶名列表,而不是創建可由get_user_ids方法訪問的實際模擬用戶對象。你的第二個答案更接近這個標記,也是我給你信用的原因。但是,我並沒有創建User模型實例,而是使用了factory_boy及其創建策略(因爲我需要用戶標識)來創建對於所討論的方法可見的用戶實例。 – William

+0

我認爲你正在做出正確的選擇 - 第一種選擇是純粹的方法,第二種選擇更實用,我會採取的。使用mock非常強大,但對於使用真實用戶對象的特定用例,將導致更簡單,更易讀的測試。 – robjohncox

相關問題