0

我最近學到了關於unittest.monkey.patch及其變體,我想用它來單元測試文件讀取函數的原子性。但是,該補丁似乎沒有任何效果。如何使用unittest修補方法io.RawIOBase.read?

這是我的設置。備受矚目的方法大致是這樣(abriged):

#local_storage.py 

def read(uri): 
    with open(path, "rb") as file_handle: 
     result = file_handle.read() 
    return result 

以及進行單元測試(也abriged)模塊:

#test/test_local_storage.py 

import unittest.mock 
import local_storage 

def _read_while_writing(io_handle, size=-1): 
    """ The patch function, to replace io.RawIOBase.read. """ 

    _write_something_to(TestLocalStorage._unsafe_target_file) #Appends "12". 
    result = io_handle.read(size) #Should call the actual read. 
    _write_something_to(TestLocalStorage._unsafe_target_file) #Appends "34". 

class TestLocalStorage(unittest.TestCase): 
    _unsafe_target_file = "test.txt" 

    def test_read_atomicity(self): 
     with open(self._unsafe_target_file, "wb") as unsafe_file_handle: 
      unsafe_file_handle.write(b"Test") 

     with unittest.mock.patch("io.RawIOBase.read", _read_while_writing): # <--- This doesn't work! 
      result = local_storage.read(TestLocalStorage._unsafe_target_file) #The actual test. 
      self.assertIn(result, [b"Test", b"Test1234"], "Read is not atomic.") 

這樣,補丁應該確保你每次嘗試讀取它,文件在實際讀取之前和之後被修改,就像它同時發生一樣,從而測試讀取的原子性。

單元測試目前成功,但我已經用打印語句驗證了補丁函數實際上並沒有被調用,所以文件從不會獲得額外的寫入(它只是說「測試」)。我也將代碼修改爲非故意的。

所以我的問題:如何修補local_storage模塊內的IO句柄的read函數?我讀過別處,人們傾向於取代open()函數來返回類似於StringIO的東西,但我不明白這是怎麼解決這個問題的。

我需要支持Python 3.4及更高版本。

+0

你看,看看如何嘲笑'open'?閱讀[this](http://www.voidspace.org.uk/python/mock/helpers.html#mock-open)。但是,有時候,我也看到'StringIO'用來不直接寫入文件系統。 – idjaw

+0

在Python 3中,它支持[here](https://docs.python.org/3/library/unittest.mock.html#mock-open) – idjaw

+0

我剛纔寫了一個解決方案,解釋瞭如何處理背景經理。它周圍的細節與你所要做的並不完全相關,但它解釋了上下文管理器的本質並使用了開放性。希望它有幫助:http:// stackoverflow。com/a/33652204/1832539 – idjaw

回答

0

我終於找到了一個解決方案。

問題是mock不能嘲笑用C編寫的任何對象方法。其中之一就是我遇到的RawIOBase

所以的確是解決方案是模擬open返回RawIOBase的包裝。我無法得到mock爲我生成一個包裝,所以我自己實現它。

有一個預先定義的文件被認爲是「不安全的」。每次調用封裝器時,封裝器都會寫入這個「不安全」的文件。這允許測試文件寫入的原子性,因爲它在寫入時將其他內容寫入不安全文件。我的實現通過寫入臨時(「安全」)文件,然後將該文件移動到目標文件上來防止此問題。

包裝有一個read函數的特殊情況,因爲要正確地測試原子性,它需要在期間寫入文件讀取。所以它在文件的第一個中途讀取,然後停止並寫入一些內容,然後讀取。這個解決方案現在是半硬編碼的(半途而廢),但我會找到一種改進方法。

你可以看到我的解決方案在這裏:https://github.com/Ghostkeeper/Luna/blob/0e88841d19737fb1f4606917f86e3de9b5b9f29b/plugins/storage/localstorage/test/test_local_storage.py