Luigi 参数默认值和模拟

Posted

技术标签:

【中文标题】Luigi 参数默认值和模拟【英文标题】:Luigi parameter default values and mocks 【发布时间】:2015-09-09 13:29:46 【问题描述】:

我正在尝试模拟为 luigi 参数提供默认值的东西。

一个愚蠢的例子展示了我想要完成的事情:

正在测试的任务:

import luigi
from bar import Bar

bar = Bar()

class Baz(luigi.Task):

    qux = luigi.Parameter(default=bar.bar())

    def baz(self):
        return self.qux;

    def foo(self):
        return bar.bar()

单元测试代码:

import unittest
from mock import Mock, patch
from sut.baz import Baz

class TestMocking(unittest.TestCase):

    def test_baz_bar(self):
        self.assertEquals("bar", Baz().baz())

    @patch('sut.baz.bar')
    def test_patched_baz(self, mock_bar):
        mock_bar.bar = Mock(return_value="foo")
        self.assertEquals("foo", (Baz().baz()))

    @patch('sut.baz.bar')
    def test_patched_foo(self, mock_bar):
        mock_bar.bar = Mock(return_value="foo")
        self.assertEquals("foo", (Baz().foo()))

luigi.Parameter 逻辑似乎比补丁更早发生。

在本例中,test_patched_foo 通过,test_patched_baz 失败。所以补丁确实发生了,但发生在来自luigi.Parameter(default=bar.bar()) 行的调用之后。

是否可以模拟和修补以这种方式调用的东西?

【问题讨论】:

在实际代码中,我通过延迟加载解决了这个问题。在我的玩具示例中复制解决方案时遇到问题,因此我不会将其作为自我答案发布。但我也不是特别喜欢这种解决方法。 【参考方案1】:

尝试将qux = luigi.Parameter(default=bar.bar()) 行移动到Baz 类的__init__ 方法中。将它放在__init__ 之外,它是在类定义时设置的,而不是实例创建,但是将它放入__init__ 将延迟它的创建到Baz 实例创建的位置。不要忘记在super 类上调用__init__

class Baz(luigi.Task):

    def __init__(self, *args, **kwargs):
        super(Baz, self).__init__(*args, **kwargs)

        self.qux = luigi.Parameter(default=bar.bar())

    ...

【讨论】:

一个有前途的想法,但与 luigi 参数的工作方式不兼容。 qux = luigi.Parameter(default=bar.bar()) 行创建了一个字段,稍后可以将其引用为 self.qux 但它不是 luigi.Parameter - 它是传递的值或默认规范的结果,在这种情况下是一个字符串。可能有一种方法可以绕过它并使用更标准的kwargs.pop 默认值,但我不确定 luigi 会喜欢它。不过可以试试看。

以上是关于Luigi 参数默认值和模拟的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 中的一个函数调用中使用默认值和任意参数?

实例化默认值和变量

在 ES6 中使用默认值和扩展语法

Jmeter之HTTP请求默认值和查看结果树使用介绍

传入值为 nil 的函数参数的默认值

ES6函数参数默认值作用域的模拟原理实现与个人的一些推测