#python #mocking
#python #издевательство
Вопрос:
Я хочу создать метод mock _subprocess
для конкретного экземпляра класса. В частности, когда задача запускается pip freeze
как команда (в этом случае она taskname
замораживается).
class Command(object):
def __init__(self, mgr, taskname, config):
self.mgr = mgr
self.taskname = taskname
self.config = config
self.append = self.config.get("append", False)
self.stderr = ""
def _subprocess(self, cmd, fnp_o, self_=None):
try:
mode = "a" if self.append else "w"
fnp_stderr = self.mgr._get_fnp("log")
with open(fnp_stderr, "a") as ferr:
ferr.write("cmd: %snstderr begin:n" % (cmd))
with open(fnp_o, mode) as fo:
proc = subprocess.check_call(
cmd.split(),
stdout=fo,
stderr=ferr,
cwd=self.mgr.workdir,
encoding="utf-8",
)
ferr.write("stderr endnn")
except (Exception,) as e:
if cpdb(): pdb.set_trace()
raise
Это метод тестирования:
def fake_subprocess(self, cmd, fnp_o, self_):
try:
raise NotImplementedError("fake_subprocess(%s)" % (locals()))
except (Exception,) as e:
pdb.set_trace()
raise
def test_001_scan(self):
try:
with patch.object(Command, '_subprocess', side_effect = self.fake_subprocess) as mock_method:
options = self.get_options()
self.mgr = Main(options)
self.mgr.process()
except (Exception,) as e:
pdb.set_trace()
raise
Моя проблема двоякая.
Во-первых, self
in fake_subprocess
ссылается на UnitTest
объект, а не на Command
объект. Мое использование self_
параметра позволяет обойти это.
Во-вторых, в большинстве случаев, за исключением pip freeze
того, что я хочу запустить исходный подпроцесс, а не поддельный.
Теперь я, вероятно, смогу справиться с этим, сохранив дополнительную ссылку на Command._subprocess
и используя self_
Но есть ли более элегантный способ? Очень наивно, когда дело доходит до unittest.Mock
.
Ответ №1:
Это то, что в итоге сработало для меня:
тестовая сторона
def fake_subprocess(self, cmd, fnp_o, self_):
try:
if self_.taskname != "freeze":
return self_._subprocess_actual(cmd, fnp_o, self_)
with open(fnp_o, self_.mode) as fo:
fo.write(self.fake_subprocess_payload["freeze"])
except (Exception,) as e:
raise
def test_001_scan(self):
try:
with patch.object(
Command, "_subprocess", side_effect=self.fake_subprocess
) as mock_method:
options = self.get_options()
self.mgr = Main(options)
self.mgr.process()
except (Exception,) as e:
raise
фактическая сторона кода
class Command(object):
def _subprocess(self, cmd, fnp_o, self_=None):
try:
fnp_stderr = self.mgr._get_fnp("log")
with open(fnp_stderr, "a") as ferr:
ferr.write("cmd: %snstderr begin:n" % (cmd))
with open(fnp_o, self.mode) as fo:
proc = subprocess.check_call(
cmd.split(), stdout=fo, stderr=ferr, cwd=self.mgr.workdir
)
ferr.write("stderr endnn")
except (Exception,) as e:
if cpdb():
pdb.set_trace()
raise
_subprocess_actual = _subprocess
def run(self):
try:
t_cmd = self.config["cmdline"] # .replace(r"\","\")
t_fnp = os.path.join(self.mgr.workdir, self.config["filename"])
fnp_log = "subprocess.log"
cmd = sub_template(t_cmd, self, self.mgr.vars)
fnp_o = sub_template(t_fnp, self, self.mgr.vars)
self._subprocess(cmd=cmd, fnp_o=fnp_o, self_=self)
except (Exception,) as e:
if cpdb():
pdb.set_trace()
raise