使用 python 单元测试模拟 os.environ

ces*_*mba 2 python unit-testing environment-variables python-unittest

我正在尝试测试一个根据给定参数为我处理工作目录的类。为此,我们使用类变量来映射它们。

当传递特定值时,将从环境变量中检索路径(请参见baz下面的示例)。这是我正在尝试测试的具体案例。

我正在使用 Python3.8.13unittest.

我试图避免:

  • 我不想模拟WorkingDirectory.map字典,因为我想确保我们从environ特定变量(BAZ_PATH)中获取。
  • 除非是唯一的解决方案,否则我想避免在测试期间编辑值,即我不想做类似的事情:os.environ["baz"] = DUMMY_BAZ_PATH

我尝试过的

我尝试按照其他出版物中的建议将其模拟environ为字典,但由于某种原因我无法使其工作。

# working_directory.py
import os


class WorkingDirectory:
    map = {
        "foo": "path/to/foo",
        "bar": "path/to/bar",
        "baz": os.environ.get("BAZ_PATH"),
    }

    def __init__(self, env: str):
        self.env = env
        self.path = self.map[self.env]

    @property
    def data_dir(self):
        return os.path.join(self.path, "data")

    # Other similar methods...
Run Code Online (Sandbox Code Playgroud)

测试文件:

# test.py

import os
import unittest

from unittest import mock

from working_directory import WorkingDirectory

DUMMY_BAZ_PATH = "path/to/baz"


class TestWorkingDirectory(unittest.TestCase):
    @mock.patch.dict(os.environ, {"BAZ_PATH": DUMMY_BAZ_PATH})
    def test_controlled_baz(self):
        wd = WorkingDirectory("baz")
        self.assertEqual(wd.path, DUMMY_BAZ_PATH)
Run Code Online (Sandbox Code Playgroud)

错误

如错误所示,os.environ返回时似乎没有正确修补Null

======================================================================
FAIL: test_controlled_baz (test_directory_structure_utils.TestWorkingDirectory)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "~/.pyenv/versions/3.8.13/lib/python3.8/unittest/mock.py", line 1756, in _inner
    return f(*args, **kw)
  File "~/Projects/dummy_project/tests/unit/test_directory_structure_utils.py", line 127, in test_controlled_baz
    self.assertEqual(wd.path, DUMMY_BAZ_PATH)
AssertionError: None != 'path/to/baz'

----------------------------------------------------------------------
Ran 136 tests in 0.325s

FAILED (failures=1, skipped=5)
Run Code Online (Sandbox Code Playgroud)

这似乎是因为BAZ_PATH实际上并不存在。不过,我希望这会没问题,因为正在修补。

当在映射字典中 时,"baz": os.environ.get("BAZ_PATH")我替换了BAZ_PATH环境中实际存在的变量,即HOME,它返回 的实际值HOME而不是DUMMY_BAZ_PATH,这使我认为我肯定做了一些错误的修补

AssertionError: '/Users/cestla' != 'path/to/baz'
Run Code Online (Sandbox Code Playgroud)

预期结果

嗯,显然,我期待着test_controlled_baz顺利通过。

小智 5

所以问题是您将 map 添加为静态变量。您的补丁工作正常,如下所示:

补丁确实有效

问题是,当它运行时已经太晚了,因为地图变量已经计算出来(在补丁之前)。如果您愿意,可以将其移至init函数,它将正常运行:

class WorkingDirectory:

def __init__(self, env: str):
    self.map = {
        "foo": "path/to/foo",
        "bar": "path/to/bar",
        "baz": os.environ.get("BAZ_PATH")
    }
    self.env = env
    self.path = self.map[self.env]
Run Code Online (Sandbox Code Playgroud)

如果由于某种原因你希望保持它静态,你还必须修补对象本身。写这样的东西就可以解决问题:

class TestWorkingDirectory(unittest.TestCase):
@mock.patch.dict(os.environ, {"BAZ_PATH": DUMMY_BAZ_PATH})
def test_controlled_baz(self):
    with mock.patch.object(WorkingDirectory, "map", {
        "foo": "path/to/foo",
        "bar": "path/to/bar",
        "baz": os.environ.get("BAZ_PATH")
    }):
        wd = WorkingDirectory("baz")
        self.assertEqual(wd.path, DUMMY_BAZ_PATH)
Run Code Online (Sandbox Code Playgroud)