使用python包装的c ++ SWIG模拟输入
Posted
技术标签:
【中文标题】使用python包装的c ++ SWIG模拟输入【英文标题】:Mocking input using python wrapped c++ SWIG 【发布时间】:2017-08-29 08:46:28 【问题描述】:我是 swig 和 python 单元测试的新手。
这就是我想要做的。我有一个需要用户输入的 c++ 函数。使用 SWIG 将 C++ 代码包装到 python 代码中。我正在尝试使用 pythons unittest 模块来模拟输入。我试过模拟 builtins.input 并用 c++ 编写我自己的基本函数,它只返回一个字符串并模拟它。
当我到达 c++ 代码中的 std::cin 时,Mocking builtins.input 仍然挂起。并模拟返回字符串的函数,不会返回模拟的 return_value。
我的猜测是由于某种原因我不能模拟函数的返回值,因为它是真正的 c++ 代码而不是真正的 python。
这是我正在使用的一些示例代码:
c++我可以包含头文件是需要的,但是真的很简单。
#include "MockTest.h"
#include <iostream>
#include <string>
MockTest::MockTest()
std::string MockTest::getInput()
std::string name;
std::cout << "Enter you name" << std::endl;
name = this->mockInput("hi"); //std::cin >> name;
std::string toReturn = "Hello " + name + " person";
return toReturn;
std::string MockTest::mockInput(std::string input)
return input;
swig 接口文件:
%module MockTest
%include "std_string.i"
%include "std_vector.i"
%include "std_iostream.i"
%
#include "MockTest.h"
%
%include <MockTest.h>
python 测试脚本
from unittest.mock import patch
from unittest import TestCase
import unittest
import MockTest
class Test(TestCase):
@patch('builtins.input', return_value="Will")
def test_Mocktest(self, input):
self.assertEqual(MockTest.MockTest().getInput(), 'Hello Will person')
@patch('MockTest.MockTest.mockInput', return_value="Will")
def test_Mocktest2(self, mockInput):
self.assertEqual(MockTest.MockTest().getInput(), 'Hello Will person')
if __name__ == '__main__':
unittest.main()
【问题讨论】:
我的 Python 版本中没有unittest.mock
,但是当删除补丁内容并重新引入 std::cin
时,这里一切正常。我很确定,使用来自unittest.mock
的patch
修补C 程序的std::cin
是不可能的。您需要告诉二进制 (.so) 文件 STDIN 不再是 STDIN。
你如何构建你的模块(哪些是命令)?因为我得到一个不同的错误(看起来模块不完整)。我正在使用(在 Win 上)Python 3.5、VStudio 2015、SwigWin 3.0.12。
【参考方案1】:
在一个愚蠢的错误上花了一些时间〜一小时后:
生成的模块不完整的事实(链接器在不向其提供 MockTest 对象时没有抱怨未定义的符号) 最后生成的是因为我忘记在 MockTest.h 的类成员之前添加public:
... X:((((
又调查了真正的问题,我终于得出一个结论:问题不是因为C++语言(至少不是直接),而是出在中介Swig 创建的层。
详情
MockTest
类(我们尝试将其mockInput
方法返回值patch
)在模块MockTest
中定义(!!!即MockTest.py !!!)由 Swig 自动生成(在我的例子中,命令是swig -o MockTest_wrap.cpp -python -c++ MockTest.i
)。这是它的定义:
class MockTest(_object):
__swig_setmethods__ =
__setattr__ = lambda self, name, value: _swig_setattr(self, MockTest, name, value)
__swig_getmethods__ =
__getattr__ = lambda self, name: _swig_getattr(self, MockTest, name)
__repr__ = _swig_repr
def __init__(self):
this = _MockTest.new_MockTest()
try:
self.this.append(this)
except __builtin__.Exception:
self.this = this
__swig_destroy__ = _MockTest.delete_MockTest
__del__ = lambda self: None
def getInput(self):
return _MockTest.MockTest_getInput(self)
def mockInput(self, input):
return _MockTest.MockTest_mockInput(self, input)
您可能已经猜到了,this mockInput
是 patch
ed,而不是来自 C++ 的那个(它的名字是 MockTest_mockInput
,它是由本机模块:_MockTest
- 本机名称是在 MocTest_wrap.cpp 中定义的 _wrap_MockTest_mockInput
)。当然,由于getInput
调用MockTest_getInput
,patch
ing mockInput
(Python 包装器)没有任何效果(就像您将其主体修改为:return "123"
)。
这是我准备的一些示例代码,以更好地说明行为(正如我在评论中提到的,我使用的是 Python 3.5.4)。请忽略最后 3 行代码并输出,直到您阅读下一段:
from unittest.mock import patch
import MockTest
if __name__ == "__main__":
return_value = "value overridden by `patch`"
mock_input_arg = "argument passed to `MockTest.mockInput`"
mock_test = MockTest.MockTest()
with patch("MockTest.MockTest.mockInput", return_value=return_value):
print("`mock_test.getInput()` returned: \"\"".format(mock_test.getInput()))
print("`mock_test.mockInput(\"\")` returned: \"\"".format(mock_input_arg, mock_test.mockInput(mock_input_arg)))
print("\nDon't mind the following output for now...\n") # SAME THING about the following code
with patch("MockTest._MockTest.MockTest_mockInput", return_value=return_value):
print("`mock_test.getInput()` returned: \"\"".format(mock_test.getInput()))
print("`mock_test.mockInput(\"\")` returned: \"\"".format(mock_input_arg, mock_test.mockInput(mock_input_arg)))
还有输出:
c:\Work\Dev\***\q45934545>"c:\Install\x64\Python\Python\3.5\python.exe" dummy.py Enter you name `mock_test.getInput()` returned: "Hello hi person" `mock_test.mockInput("argument passed to `MockTest.mockInput`")` returned: "value overridden by `patch`" Don't mind the following output for now... Enter you name: `mock_test.getInput()` returned: "Hello hi person" `mock_test.mockInput("argument passed to `MockTest.mockInput`")` returned: "value overridden by `patch`"
然后我进一步尝试patch
MockTest._MockTest.MockTest_mockInput
,但我得到了相同的输出,因为我没有patch
MockTest::mockInput
(来自MockTest.cpp)而是@987654346 @(来自 MocTest_wrap.cpp)。
下面是mockInput
的C++ 代码和Python 之间所有层的表格(getInput
完全相同):
当getInput
调用mockInput
(layer 0)时,patch
应该发生在哪里,但不幸的是,Python 无法使用。
我想到的第一个st解决方案是直接patch
ing getInput
(第1层)(如果在很多地方使用的话)。
【讨论】:
你可以创建你的 c++ 源代码,这样你就可以用另一个流替换std::cin
,你可以从 Python 控制它
感谢@JensMunk 的建议,此时我不确定我知道该怎么做;如果需要,我一定会探索这种方法!以上是关于使用python包装的c ++ SWIG模拟输入的主要内容,如果未能解决你的问题,请参考以下文章
swig -c++ 选项破坏纯 C 文件的包装,错误 C3861:unresolved externals
通过 SWIG 将 Python 3 个字节输入到 C char*
SWIG 在 Windows 中生成 C++ Python3 包装器导致断言 MSVC 2017