56

Python でクロスプラットフォーム ファイルシステムのモックを行う標準的な方法 (サード パーティのライブラリをインストールせずに) はありますか? サードパーティのライブラリを使用する必要がある場合、どのライブラリが標準ですか?

4

4 に答える 4

44

pyfakefs (ホームページ) は、あなたが望むことを行います –偽のファイルシステムです。サードパーティですが、そのパーティは Google です。使用方法については、テスト中のモジュールのファイル アクセス参照を置き換える方法を参照してください。

モッキングの場合、unittest.mockは Python 3.3+ ( PEP 0417 )の標準ライブラリです。以前のバージョンについては、PyPI: mock (for Python 2.5+) (ホームページ) を参照してください。

テストとモックの用語には一貫性がありません。Gerard Meszaros のTest Double用語を使用すると、「偽物」、つまりファイル システムのように動作するもの (ファイルを作成、開く、削除できる) を求めていますが、実際のファイル システムではありません (この場合は、そのため、テスト ファイルや一時ディレクトリは必要ありません。

従来のモッキングでは、代わりにシステム コールをモックアウトします (Python では、osモジュール内の関数をモック アウトします。たとえば、os.rmos.listdir)。

于 2014-10-20T03:07:29.227 に答える
20

pytestは多くの注目を集めており、tmpdirモンキーパッチ(モック) を使用してこれらすべてを実行できます。

tmpdirベース一時ディレクトリ (デフォルトでは、システム一時ディレクトリのサブディレクトリとして作成される) に作成される、テスト呼び出しに固有の一時ディレクトリを提供する関数引数を使用できます。

import os
def test_create_file(tmpdir):
    p = tmpdir.mkdir("sub").join("hello.txt")
    p.write("content")
    assert p.read() == "content"
    assert len(tmpdir.listdir()) == 1

monkeypatchfunction 引数は、属性、辞書項目、または環境変数を安全に設定/削除したりsys.path、インポート用に変更したりするのに役立ちます。

import os
def test_some_interaction(monkeypatch):
    monkeypatch.setattr(os, "getcwd", lambda: "/")

ラムダを使用する代わりに関数を渡すこともできます。

import os.path
def getssh(): # pseudo application code
    return os.path.join(os.path.expanduser("~admin"), '.ssh')

def test_mytest(monkeypatch):
    def mockreturn(path):
        return '/abc'
    monkeypatch.setattr(os.path, 'expanduser', mockreturn)
    x = getssh()
    assert x == '/abc/.ssh'

# You can still use lambda when passing arguments, e.g.
# monkeypatch.setattr(os.path, 'expanduser', lambda x: '/abc')

アプリケーションがファイル システムと多くの対話を行う場合、モック化は退屈で反復的なものになるため、pyfakefsのようなものを使用する方が簡単かもしれません。

于 2014-11-11T09:11:55.453 に答える
14

Python 3.3 以降の標準のモック フレームワークは unittest.mock です。これは、ファイルシステムまたはその他のものに使用できます。

サルのパッチを使ってモックすることで、単純に手でロールすることもできます。

些細な例:

import os.path
os.path.isfile = lambda path: path == '/path/to/testfile'

もう少しいっぱい (未テスト):

import classtobetested                                                                                                                                                                                      
import unittest                                                                                                                                                                                             

import contextlib                                                                                                                                                                                           

@contextlib.contextmanager                                                                                                                                                                                  
def monkey_patch(module, fn_name, patch):                                                                                                                                                                   
    unpatch = getattr(module, fn_name)                                                                                                                                                                      
    setattr(module, fn_name)                                                                                                                                                                                
    try:                                                                                                                                                                                                    
        yield                                                                                                                                                                                               
    finally:                                                                                                                                                                                                
        setattr(module, fn_name, unpatch)                                                                                                                                                                   


class TestTheClassToBeTested(unittest.TestCase):                                                                                                                                                              
    def test_with_fs_mocks(self):                                                                                                                                                                           
        with monkey_patch(classtobetested.os.path,                                                                                                                                                          
                          'isfile',                                                                                                                                                                         
                          lambda path: path == '/path/to/file'):                                                                                                                                            
            self.assertTrue(classtobetested.testable())                 

この例では、実際のモックは些細なものですが、保存や削除などのファイルシステム アクションを表すことができるように、状態を持つものでそれらをバックアップできます。はい、コードで基本的なファイルシステムを複製/シミュレートする必要があるため、これはすべて少し醜いです。

Python ビルトインにモンキー パッチを適用することはできないことに注意してください。そうは言っても…

以前のバージョンでは、可能な限りサードパーティのライブラリを使用する場合は、 PEP 0417のおかげで 3.3+ 以降の標準ライブラリにあるMichael Foord の素晴らしいMockを使用します。Python 2.5+ のPyPIで入手できます。 . そして、ビルトインをモックできます!unittest.mock

于 2013-10-30T01:40:35.143 に答える