2017-06-01 44 views
1

我經常需要編寫一個命令行腳本,它將讀取數據庫,執行一些分析並將結果寫回數據庫。我努力讓一般脫鉤,並創建一個單獨的數據層是寫劇本load.pywrite.pydo_analytics.py其中負載和寫做數據庫的交互,和do_analytics.py文件是這樣的:在Python中測試數據庫讀/寫加分析腳本

import load 
import write 

def batch_classify(model_filepath='my_model.pkl'): 
    with open(model_filepath, 'rb') as infile: 
     model = pickle.load(infile) 

    data_loader = load.DataLoader() 
    data_loader.load_data() 
    data_loader.clean_data() 
    data = data_loader.data 
    # Maybe do some more manipulations here... 
    output = model.transform(data) 

    data_writer = write.DataWriter() 
    data_writer.write_data(output) 

if __name__ == "__main__": 
    # maybe would have some command line options here to pass to batch_classify 
    batch_classify() 

我現在想測試一些固定的數據集,並確保分類(輸出)結果是我所期望的。我現在不需要測試實際的數據庫連接,所以基於一些研究,我想我想嘲笑this post,但我不確定應該嘲笑什麼級別的對象,如何正確地重構以實際測試一旦我有了嘲弄的對象,如果這是最好的辦法。當以前出現這種情況時,我已經通過實際數據庫中的小型固定測試表獲得解決方案,但它絕不是優雅或乾淨的代碼。

回答

0

在你的情況下,我會有4個文件。

database_provider.py

class DatabaseProvider(object): 
    def get_data(self): 
     return db.get() # Get your data 

    def set_data(self, data): 
     db.set(data) # update your data 

analytic_manager.py

class AnalyticManager(object): 
    def __init__(self): 
     self.database_provider = DatabaseProvider() 

    def process(self, arguments): 
     # Get data from DB 
     data = self.database_provider.get_data() 

     # Do your logic here 
     data = self.clean(data) 
     data = self.transform(data) 

     # Save in DB 
     self.database_provider.set_data(data) 

    def clean(self, data): 
     # do cleaning 
     return cleaned_data 

    def transform(self, data): 
     # do transform 
     return transformed_data 

main.py

if __name__ == "__main__": 
    arguments = whatever 
    manager = AnalyticManager(arguments) 
    manager.process(arguments) 

test_analytic_manager.py

import unittest 
from mock import Mock, patch 

class TestAnalyticManager(unittest.TestCase): 


    @patch("database_provider.DatabaseProvider.get_data") 
    @patch("database_provider.DatabaseProvider.set_data") 
    def test_process_should_clean_and_transform_data(self, mock_set_data, mock_get_data): 
     # Arranges 
     arguments = whatever 
     manager = AnalyticManager(arguments) 

     mock_get_data.return_value = ["data from DB", "data2 from DB"] 

     expected_data = ["cleaned and transformed data1", "cleaned and transformed data2"] 

     # Acts 
     manager.process(arguments) 

     # Asserts 
     mock_set_data.assert_called_once_with(expected_data) 

現在可以,如果你想嘲笑你的供應商。 最重要的是在經理內部完成所有的邏輯,而不是提供者。 您的db_provider應該只與您的數據庫進行交互並將接收到的數據映射到您的python對象。

管理器和提供程序層對於模擬非常重要。 單獨的責任將避免有一個意大利麪代碼。

+0

謝謝,這看起來不錯。爲了澄清,最好的做法是選擇將DatabaseProvider對象 - 實型還是模型 - 傳遞給實例化的AnalyticManager? – elphz

+0

在Python中,您不必在單元測試中傳遞選項進行嘲諷。請參閱test_analytic_manager.py。在實現數據庫提供程序之前,您可以使用此類來測試您的邏輯, – M07