如何通过测试修改class数据?
How to modify class data through tests?
我有这个 luigi 任务,它需要的输入是 'test_file.txt'。我想通过测试 class 来更改所需的输入。我需要更改输入,以便我可以使用文件测试 class 的功能。我尝试了下面的代码,但打印的结果仍然是初始路径-'test_file.txt'。如何仅在测试中更改路径? (得到 'data.json' 作为打印结果。
import pytest
import luigi
class LuigiToBeTested(luigi.ExternalTask):
def requires(self):
return luigi.LocalTarget("test_file.txt")
def test_Luigi():
class_instance = LuigiToBeTested()
class_instance.requires().path = 'data.json'
print('/////', class_instance.requires().path) #to get data.json
这里有 3 个解决方案供您选择
test_luigi_1
- 拦截(通过函数)所有调用以初始化 luigi.LocalTarget
的新对象并替换输入文件名。
test_luigi_2
- 与 1 相同,但通过子类
test_luigi_3
- 不要像 1 和 2 中那样进行修补,而是通过允许 dependency injection 重新设计您的源代码以使其可测试。这是更可扩展的方式,也是更适合未来的方式。
一个。 Ask for things, Don't look for things (aka Dependency Injection / Law of Demeter)
b。 Fixing the Client API: Dependency Injection
c。还有更多关于为什么这是首选方式的参考资料。
import luigi
import pytest
class LuigiToBeTested(luigi.ExternalTask):
def requires(self):
return luigi.LocalTarget("test_file.txt")
class LuigiToBeTested2(luigi.ExternalTask):
def requires(self, file_name):
return luigi.LocalTarget(file_name)
@pytest.fixture
def amend_local_target_func_based(mocker): # The <mocker> fixture is from pytest-mock. Install via <pip install pytest-mock>.
orig = luigi.LocalTarget
def fake(file_name):
print(f"Intercepted initialization of luigi.LocalTarget via function. The file {file_name} might be replaced.")
# If you only want to replace 'test_file.txt' but not the other files, then use this.
# Otherwise, remove this if and just read from 'data.json' always.
if file_name == 'test_file.txt':
file_name = 'data.json'
return orig(file_name)
mocker.patch('luigi.LocalTarget', new=fake)
@pytest.fixture
def amend_local_target_class_based(mocker):
class LocalTargetStub(luigi.LocalTarget):
def __init__(self, file_name):
print(f"Intercepted initialization of luigi.LocalTarget via subclass. The file {file_name} might be replaced.")
# If you only want to replace 'test_file.txt' but not the other files, then use this.
# Otherwise, remove this if and just read from 'data.json' always.
if file_name == 'test_file.txt':
file_name = 'data.json'
super().__init__(file_name)
mocker.patch('luigi.LocalTarget', new=LocalTargetStub)
def test_luigi_1(amend_local_target_func_based):
class_instance = LuigiToBeTested()
print('/////', class_instance.requires().path) #to get data.json
def test_luigi_2(amend_local_target_class_based):
class_instance = LuigiToBeTested()
print('/////', class_instance.requires().path) #to get data.json
def test_luigi_3():
class_instance = LuigiToBeTested2()
print('/////', class_instance.requires('data.json').path) #to get data.json
$ pytest -q -rP
================================================================================================= PASSES ==================================================================================================
______________________________________________________________________________________________ test_luigi_1 _______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
Intercepted initialization of luigi.LocalTarget via function. The file test_file.txt might be replaced.
///// data.json
______________________________________________________________________________________________ test_luigi_2 _______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
Intercepted initialization of luigi.LocalTarget via subclass. The file test_file.txt might be replaced.
///// data.json
______________________________________________________________________________________________ test_luigi_3 _______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
///// data.json
3 passed, 1 warning in 0.07s
我有这个 luigi 任务,它需要的输入是 'test_file.txt'。我想通过测试 class 来更改所需的输入。我需要更改输入,以便我可以使用文件测试 class 的功能。我尝试了下面的代码,但打印的结果仍然是初始路径-'test_file.txt'。如何仅在测试中更改路径? (得到 'data.json' 作为打印结果。
import pytest
import luigi
class LuigiToBeTested(luigi.ExternalTask):
def requires(self):
return luigi.LocalTarget("test_file.txt")
def test_Luigi():
class_instance = LuigiToBeTested()
class_instance.requires().path = 'data.json'
print('/////', class_instance.requires().path) #to get data.json
这里有 3 个解决方案供您选择
test_luigi_1
- 拦截(通过函数)所有调用以初始化luigi.LocalTarget
的新对象并替换输入文件名。test_luigi_2
- 与 1 相同,但通过子类test_luigi_3
- 不要像 1 和 2 中那样进行修补,而是通过允许 dependency injection 重新设计您的源代码以使其可测试。这是更可扩展的方式,也是更适合未来的方式。一个。 Ask for things, Don't look for things (aka Dependency Injection / Law of Demeter)
b。 Fixing the Client API: Dependency Injection
c。还有更多关于为什么这是首选方式的参考资料。
import luigi
import pytest
class LuigiToBeTested(luigi.ExternalTask):
def requires(self):
return luigi.LocalTarget("test_file.txt")
class LuigiToBeTested2(luigi.ExternalTask):
def requires(self, file_name):
return luigi.LocalTarget(file_name)
@pytest.fixture
def amend_local_target_func_based(mocker): # The <mocker> fixture is from pytest-mock. Install via <pip install pytest-mock>.
orig = luigi.LocalTarget
def fake(file_name):
print(f"Intercepted initialization of luigi.LocalTarget via function. The file {file_name} might be replaced.")
# If you only want to replace 'test_file.txt' but not the other files, then use this.
# Otherwise, remove this if and just read from 'data.json' always.
if file_name == 'test_file.txt':
file_name = 'data.json'
return orig(file_name)
mocker.patch('luigi.LocalTarget', new=fake)
@pytest.fixture
def amend_local_target_class_based(mocker):
class LocalTargetStub(luigi.LocalTarget):
def __init__(self, file_name):
print(f"Intercepted initialization of luigi.LocalTarget via subclass. The file {file_name} might be replaced.")
# If you only want to replace 'test_file.txt' but not the other files, then use this.
# Otherwise, remove this if and just read from 'data.json' always.
if file_name == 'test_file.txt':
file_name = 'data.json'
super().__init__(file_name)
mocker.patch('luigi.LocalTarget', new=LocalTargetStub)
def test_luigi_1(amend_local_target_func_based):
class_instance = LuigiToBeTested()
print('/////', class_instance.requires().path) #to get data.json
def test_luigi_2(amend_local_target_class_based):
class_instance = LuigiToBeTested()
print('/////', class_instance.requires().path) #to get data.json
def test_luigi_3():
class_instance = LuigiToBeTested2()
print('/////', class_instance.requires('data.json').path) #to get data.json
$ pytest -q -rP
================================================================================================= PASSES ==================================================================================================
______________________________________________________________________________________________ test_luigi_1 _______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
Intercepted initialization of luigi.LocalTarget via function. The file test_file.txt might be replaced.
///// data.json
______________________________________________________________________________________________ test_luigi_2 _______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
Intercepted initialization of luigi.LocalTarget via subclass. The file test_file.txt might be replaced.
///// data.json
______________________________________________________________________________________________ test_luigi_3 _______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
///// data.json
3 passed, 1 warning in 0.07s