|
楼主 |
发表于 2020-4-19 23:09:03
|
显示全部楼层
Testing
最后:你如何测试你的库?现在,显然有很多用C和C++编写的测试工具,但是我认为最好的工具实际上是在其他地方。
共享库不仅仅是C和C++所能享受的,你可以用各种语言使用它们。
有什么更好的方法来测试你的API,从一个不是C++的语言使用它?
在我的例子中,我使用Python来测试我的库。更重要的是:我使用py.test和CFFI来测试我的库。这比直接在C/C++中做的有很多优点。
最大的优点是提高了迭代速度。我根本不需要编译我的测试,它们只是运行。
不仅编译步骤会消失,我还可以利用Python的动态类型和py.test良好的assert语句。
我自己编写帮助程序来打印信息,并在我的库和Python之间转换数据,我获得了良好的错误报告的所有好处。
第二个好处是很好的隔离。pytest-xdist是py.test的插件,它将--boxed标志添加到py.test中,py.test在单独的进程中运行每个测试。
如果你有可能由于segfault而崩溃的测试,那么这是非常有用的。
如果在系统上启用coredumps,那么可以在gdb中加载segfault并找出问题所在。这也非常有效,因为您不需要处理由于断言失败和代码跳过清理而发生的内存泄漏。
操作系统将分别为每个测试清理。不幸的是,这是通过fork()系统调用实现的,所以它现在在windows上不能很好地工作。
那么,你如何使用你的库与CFFI?
您需要做两件事:您需要确保您的公共头文件不包含任何其他头文件。
如果您不能,只需添加一个禁用include的定义(如YL\u NOINCLUDE)。
(译者注:CFFI(C Foreign Function Interface) 是Python的C语言外部函数接口。 Python可以与几乎任何C语言代码进行交互,基于类似C语言的声明,您通常可以从头文件或文档中复制粘贴。)
This is all that's needed to make CFFI work:
- import os
- import subprocess
- from cffi import FFI
- here = os.path.abspath(os.path.dirname(__file__))
- header = os.path.join(here, 'include', 'yourlibrary.h')
- ffi.cdef(subprocess.Popen([
- 'cc', '-E', '-DYL_API=', '-DYL_NOINCLUDE',
- header], stdout=subprocess.PIPE).communicate()[0])
- lib = ffi.dlopen(os.path.join(here, 'build', 'libyourlibrary.dylib'))
复制代码
Place it in a file called testhelpers.py next to your tests.
很明显,这是一个只在OSX上运行的简单版本,但是很容易扩展到不同的操作系统。
实际上,这会调用C预处理器并添加一些额外的定义,然后将其返回值馈送给CFFI解析器。之后,你有一个漂亮的包装库工作。
下面是这样一个测试的例子。把它放在一个名为test_something.py的文件中,让py.test执行它:
- import time
- from testhelpers import ffi, lib
- def test_basic_functionality():
- task = lib.yl_task_new()
- while lib.yl_task_is_pending(task)
- lib.yl_task_process(task)
- time.sleep(0.001)
- result = lib.yl_task_get_result_string(task)
- assert ffi.string(result) == ''
- lib.yl_task_free(task)
复制代码
py.test还有其他优点。例如,它支持fixture,允许您设置可以在测试之间重用的公共资源。
这是非常有用的,例如,如果使用库需要创建某种上下文对象,在其上设置公共配置,然后销毁它。
To do that, just create a conftest.py file with the following content:
- import pytest
- from testhelpers import lib, ffi
- @pytest.fixture(scope='function')
- def context(request):
- ctx = lib.yl_context_new()
- lib.yl_context_set_api_key(ctx, "my api key")
- lib.yl_context_set_debug_mode(ctx, 1)
- def cleanup():
- lib.yl_context_free(ctx)
- request.addfinalizer(cleanup)
- return ctx
- To use this now, all you need to do is to add a parameter called context to your test function:
- from testhelpers import ffi, lib
- def test_basic_functionality(context):
- task = lib.yl_task_new(context)
- ...
复制代码
|
|