![](/img/trans.png)
[英]How to not instantiate dataclass members as class variables in Python?
[英]How to test dataclass that can be initialized with environment variables?
我有以下數據類:
import os
import dataclasses
@dataclasses.dataclass
class Example:
host: str = os.environ.get('SERVICE_HOST', 'localhost')
port: str = os.environ.get('SERVICE_PORT', 30650)
我該如何為此編寫測試? 我嘗試了以下看起來應該可以工作的方法:
from stackoverflow import Example
import os
def test_example(monkeypatch):
# GIVEN environment variables are set for host and port
monkeypatch.setenv('SERVICE_HOST', 'server.example.com')
monkeypatch.setenv('SERVICE_PORT', '12345')
# AND a class instance is initialized without specifying a host or port
example = Example()
# THEN the instance should reflect the host and port specified in the environment variables
assert example.host == 'server.example.com'
assert example.port == '12345'
但這失敗了:
====================================================================== test session starts ======================================================================
platform linux -- Python 3.8.12, pytest-7.1.2, pluggy-1.0.0
rootdir: /home/biogeek/tmp
collected 1 item
test_example.py F [100%]
=========================================================================== FAILURES ============================================================================
_________________________________________________________________________ test_example __________________________________________________________________________
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f39de559220>
def test_example(monkeypatch):
# GIVEN environment variables are set for host and port
monkeypatch.setenv('SERVICE_HOST', 'server.example.com')
monkeypatch.setenv('SERVICE_PORT', '12345')
# AND a class instance is initialized without specifying a host or port
example = Example()
# THEN the instance should reflect the host and port specified in the environment variables
> assert example.host == 'server.example.com'
E AssertionError: assert 'localhost' == 'server.example.com'
E - server.example.com
E + localhost
test_example.py:12: AssertionError
==================================================================== short test summary info ====================================================================
FAILED test_example.py::test_example - AssertionError: assert 'localhost' == 'server.example.com'
======================================================================= 1 failed in 0.05s =======================================================================
您的測試失敗,因為您的代碼在導入模塊時加載了環境變量。 模塊級代碼很難測試,因為設置默認值的os.environ.get()
調用在測試運行之前已經運行。 您必須從sys.modules
模塊緩存中有效地刪除您的模塊,並且僅在 mocking 退出os.environ
環境變量之后導入您的模塊,以測試導入時發生的情況。
您可以改為使用帶有default_factory
參數的dataclass.field()
; 每當您創建數據類的實例時,它都會執行一個可調用以獲得默認值:
import os
from dataclasses import dataclass, field
from functools import partial
@dataclass
class Example:
host: str = field(default_factory=partial(os.environ.get, 'SERVICE_HOST', 'localhost'))
port: str = field(default_factory=partial(os.environ.get, 'SERVICE_PORT', '30650'))
我使用functools.partial()
object創建了一個調用os.environ.get()
的給定名稱和默認值。
請注意,我還將SERVICE_PORT
的默認值更改為字符串; 畢竟, port
字段被注釋為str
,而不是int
。 :-)
如果您必須在導入時從環境變量中設置這些默認值,那么您可以讓pytest
在conftest.py
模塊中模擬出這些環境變量; 這些是在導入測試之前導入的,因此您有機會在導入被測模塊之前進行調整。 但是,這不會讓您使用不同的默認值運行多個測試:
# add to conftest.py at the same package level, or higher.
@pytest.fixture(autouse=True, scope="session")
def mock_environment(monkeypatch):
monkeypatch.setenv('SERVICE_HOST', 'server.example.com')
monkeypatch.setenv('SERVICE_PORT', '12345')
上面的夾具示例,當放置在conftest.py
中時,將在加載測試之前自動修補您的環境,因此在導入模塊之前,並且此補丁在測試 session 結束時自動撤消。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.