【问题标题】:Python mock Patch os.environ and return valuePython mock Patch os.environ 和返回值
【发布时间】:2015-10-13 11:40:33
【问题描述】:

使用模拟单元测试 conn():

app.py

import mysql.connector
import os, urlparse


def conn():
    if "DATABASE_URL" in os.environ:
        url = urlparse(os.environ["DATABASE_URL"])
        g.db = mysql.connector.connect(
            user=url.username,
            password=url.password,
            host=url.hostname,
            database=url.path[1:],
        )
    else:
        return "Error"

test.py

def test_conn(self):
    with patch(app.mysql.connector) as mock_mysql:
        with patch(app.os.environ) as mock_environ:
            con()
            mock_mysql.connect.assert_callled_with("credentials")

错误:断言 mock_mysql.connect.assert_called_with 未被调用。

我认为这是因为 'Database_url' 不在我修补的 os.environ 中,并且没有对 mysql_mock.connect 进行测试调用。

问题:

1 我需要进行哪些更改才能使此测试代码正常工作?

2.我也必须修补'urlparse'吗?

【问题讨论】:

    标签: python unit-testing python-3.x dependency-injection mocking


    【解决方案1】:

    您可以尝试unittest.mock.patch.dict 解决方案。只需使用 dummy 参数调用 conn

    import mysql.connector
    import os, urlparse
    
    
    @mock.patch.dict(os.environ, {"DATABASE_URL": "mytemp"}, clear=True)  # why need clear=True explained here https://stackoverflow.com/a/67477901/248616
    def conn(mock_A):
        print os.environ["mytemp"]
        if "DATABASE_URL" in os.environ:
            url = urlparse(os.environ["DATABASE_URL"])
            g.db = mysql.connector.connect(
                user=url.username,
                password=url.password,
                host=url.hostname,
                database=url.path[1:],
            )
        else:
            return "Error"
    

    或者,如果您不想修改原始功能,请尝试以下解决方案:

    def func():
        print os.environ["mytemp"]
    
    
    def test_func():
        k = mock.patch.dict(os.environ, {"mytemp": "mytemp"})
        k.start()
        func()
        k.stop()
    
    
    test_func()
    

    【讨论】:

    • 谢谢伙计,我后来意识到这一点,并在删除我的评论后不久。非常感谢它正在工作,但我很困惑使用上述方法如何将 {'mytemp':'mytemp'} 传递到操作系统.环境
    • 非常感谢,我接受了这个答案,当我获得 15 个声望时,我会投票赞成这个答案。
    • 由于对问题/答案没有影响,我从两者中删除了错误的 Python 代码:-)
    • @MartinThoma 哦,非常感谢 :)
    • 很好的答案!!!
    【解决方案2】:

    在我的用例中,我试图模拟没有设置环境变量。为此,请确保将 clear=True 添加到您的补丁中。

    with patch.dict(os.environ, {}, clear=True):
        func()
    

    【讨论】:

    • 这正是我所缺少的,谢谢!
    【解决方案3】:

    在导入模块之前,在文件的头部模拟环境:

    with patch.dict(os.environ, {'key': 'mock-value'}):
        import your.module
    

    【讨论】:

      【解决方案4】:

      接受的答案是正确的。这是一个装饰器@mockenv 来做同样的事情。

      def mockenv(**envvars):
          return mock.patch.dict(os.environ, envvars)
      
      
      @mockenv(DATABASE_URL="foo", EMAIL="bar@gmail.com")
      def test_something():
          assert os.getenv("DATABASE_URL") == "foo"
      
      

      【讨论】:

        【解决方案5】:

        为此,我发现pytest's monkeypatch fixture 在需要设置环境变量时会带来更好的代码:

        def test_conn(monkeypatch):
            monkeypatch.setenv('DATABASE_URL', '<URL WITH CREDENTIAL PARAMETERS>')
            with patch(app.mysql.connector) as mock_mysql:
                conn()
            mock_mysql.connect.assert_called_with(<CREDENTIAL PARAMETERS>)
        

        【讨论】:

        • 这给了我AttributeError: 'MockFixture' object has no attribute 'setenv'
        • 这对我有用,使用pytest==3.5.0。 @SonicSoul 也许你的版本不一样?
        • @SonicSoul 确保在测试参数中包含monkeypatch
        【解决方案6】:

        您还可以使用类似question 中描述的modified_environ 上下文管理器来设置/恢复环境变量。

        with modified_environ(DATABASE_URL='mytemp'):
            func()
        

        【讨论】:

          猜你喜欢
          • 2015-09-09
          • 2014-09-08
          • 2017-04-03
          • 2017-07-22
          • 1970-01-01
          • 2014-10-18
          • 1970-01-01
          • 2015-06-19
          • 2021-09-05
          相关资源
          最近更新 更多