2016-04-29 21 views
3

我想爲因爲我不想單元,使用自定義pyodbc數據庫連接懲戒Django的單元pyodbc模塊調用測試

views.py

from django.http import JsonResponse, HttpResponseNotFound, HttpResponseBadRequest, HttpResponseServerError, HttpResponseForbidden 
from django.core.exceptions import SuspiciousOperation 
from django.utils.datastructures import MultiValueDictKeyError 
import os 
import pyodbc 

# Create your views here. 

db_credentials = os.environ.get('DATABASE_CREDENTIALS') 
dbh = pyodbc.connect(db_credentials) 

def get_domains(request): 
    if request.method == 'GET': 
     args = request.GET 
    elif request.method == 'POST': 
     args = request.POST 

    try: 
     cursor = dbh.cursor() 
     if 'owner' in args: 
      owner = args['owner'] 
      cursor.execute('{call GET_DOMAINS_FOR_OWNER(?)}', owner) 
     else: 
      cursor.execute('{call GET_DOMAINS()}') 
     result = cursor.fetchall() 
     if(result): 
      return JsonResponse([row[0] for row in result], safe=False) 
     else: 
      return JsonResponse([], safe=False) 
    except pyodbc.Error as e: 
     return HttpResponseServerError(e) 
    except SuspiciousOperation as e: 
     return HttpResponseForbidden(e) 

一些Django的意見單元測試測試將進入數據庫,我怎麼能嘲笑的行爲因爲:

  • 因爲pyodbc的模擬庫將無法正常工作是一個Python C擴展
  • 使用sys.modules中似乎並沒有工作,可能是因爲該模塊在views.py被使用,而不是tests.py

這裏是我的測試車手

tests.py

from django.test import SimpleTestCase 
from sms_admin import * 

# Create your tests here. 


HTTP_OK = 200 
HTTP_NOTFOUND = 404 


class AdminTestCase(SimpleTestCase): 
    """docstring for AdminTestCase""" 

    def test_get_pool_for_lds(self): 
     response = self.client.get('/sms_admin/get_pool_for_lds', {'domain': 'sqlconnect', 'stage': 'dev', 'lds': 'reader'}) 
     self.assertEqual(response.content, b'pdss_reader') 
     self.assertEqual(response.status_code, HTTP_OK) 

回答

1

您可以修補pyodbc.connect沒有任何限制,因爲在後續的例子表明:

import pyodbc 
from unittest.mock import patch 

with patch("pyodbc.connect") as mock_connect: 
    pyodbc.connect("Credentials") 
    mock_connect.assert_called_with("Credentials") 

現在真正的問題在view.py是同時您導入view.py執行該線路的線路

dbh = pyodbc.connect(db_credentials) 

,你不能控制它沒有實現某種黑客在你的測試代碼像打補丁進口view.py或其他任何導入前連接它。

我想強烈地勸阻你寫這種骯髒的技巧,並改變你的代碼來實現一個懶惰的dbh屬性。另一種方法可以編寫自己的db class wrapper(更好),並將其修補到測試中,但這是一個強大的設計更改,您可以稍後通過實施測試的功能來引入它。

view.py使用:

_dbh = None 
def get_db(): 
    global _dbh 
    if _dbh is None: 
     _dbh = pyodbc.connect(db_credentials) 
    return _dbh 

其中cusror成爲

cursor = get_db().cursor() 

現在你可以修補get_db()並在測試使用return_value模擬

class AdminTestCase(SimpleTestCase): 
    """docstring for AdminTestCase""" 

    def setUp(self): 
     super().setUp() 
     p = patch("yourpackage.view.get_db") 
     self.addCleanup(p.stop) 
     self.get_db_mock = p.start() 
     self.db_mock = self.get_db_mock.return_value 
     self.cursor_mock = self.db_mock.cursor.return_value 

    def test_get_pool_for_lds(self, get_db_mock): 
     .... configure self.cursor_mock to behave as you need 

     response = self.client.get('/sms_admin/get_pool_for_lds', {'domain': 'sqlconnect', 'stage': 'dev', 'lds': 'reader'}) 
     self.assertEqual(response.content, b'pdss_reader') 
     self.assertEqual(response.status_code, HTTP_OK) 

我離開的細節如何mock_cursor應該表現出來,並且遊標調用聲明。你可以通過閱讀mock framework documentation來寫。我以前用setUp()方法修補連接,因爲我可以猜測,在幾乎所有的測試中都需要它,其中cursor_mockdb_mockget_db_mock可以用於不同的行爲:我的經驗是,這種方法在你會增加更多的測試。

+0

在這種情況下,db_mock應該是另一個Mock對象嗎? – tiagovrtr

+0

db_mock是另一種模擬,它與修補程序get_db在測試環境中的返回值相同 –

+0

@tiagovrtr我試圖讓我的答案更清晰。 –