英文:
What buildsystem design techniques are there (using CMake) to test functionality that is internal to my shared library?
问题
以下是翻译好的部分:
有可能在共享库中隐藏一些符号,如果它们不被认为会被外部调用的话。但是,虽然这些符号对于库的客户端来说并不是必需的,它们可能需要用于测试。
例如,在我的情况下,我有一个具有小型公共API和大量内部逻辑的库。为了编写内部逻辑的单元测试,我(认为我)必须将共享库链接到测试可执行文件,并以某种方式调用隐藏的函数/类。
我一直在使用的解决方案是在CMake中引入一个名为"build_tests"的变量,如果它为ON
,则所有符号默认对于共享库而言都是可见的。否则,我会将它们隐藏起来。
还有哪些其他处理这个问题的方法,它们之间有什么不同?(它们做出了什么权衡?)
英文:
There is a possibility to hide some symbols in a shared library if they are not considered to be called from outside. But while the symbols are not required for the client of the library, they may be needed for tests.
For example, in my case, I have a library with a small public api and large inner logic. In order to write unit tests for the inner logic, I (think I) have to link the shared library to the test executable and somehow call hidden functions/classes.
The solution I've been using is to introduce a variable in CMake "build_tests" and if it is ON
, then all symbols default to being visible for shared libraries. Otherwise I hide them.
What other ways exist to deal with this issue, and how do they compare? (what tradeoffs do they make?)
答案1
得分: 1
我的解决方案是在CMAKE中引入一个名为"build_tests"的变量,如果它为ON,则不隐藏符号。
这样做的不利之处在于,对于一个生成的构建系统,基本上你必须在发布时选择是否希望看到符号(按需可见),以及对于测试而言对你是否可用。你必须为测试生成一个构建系统,以及一个用于发布的构建系统(如果我理解正确的话),这似乎并不理想。如果你创建一个可用于测试构建的可见性宏,情况也会不理想。
有评论中提到,有些人认为不需要测试内部部分,只有库接口的一部分需要/应该被测试。我认为测试库的较低级别部分对于测试来说也是有用的,尽管它们不是库的接口的一部分。
问题在于,对于一个单一的生成构建系统中的单个共享库目标,每个符号要么可见,要么不可见。因此,如果你想要只生成一个构建系统,其中你构建共享库而不妥协于从中可见什么,但又能够测试其内部部分,你需要改变构建共享库的方式,或者改变构建测试可执行文件的方式。
实际操作方式将取决于你的库的特征以及你想要测试的内部部分。
你可以尝试将内部逻辑重构为自己的静态库,并让共享库包装静态库。然后共享库将成为你的项目用户使用的部分,而静态库将成为你用于测试的部分,其中你不受任何可见性机制的限制。你要测试的二进制文件将是一个链接到静态库的测试可执行文件,因此如果你启用了任何优化,有些事情在编译时会有差异,所以要注意这一点。这种方法可能需要更多的编程工作来进行分离和包装。
另一种考虑的方法是将实现库的内部逻辑的特定源文件添加到专用的测试可执行文件中(或者对于不同的源文件添加到多个测试可执行文件中)。如果你不使用链接时优化,并且想要以非常细粒度的方式进行操作,比如每个源文件一个测试可执行文件,这可能会导致比前面提到的使用静态库的方法更多的编译时间,再次强调差异的细节将取决于你的项目的特点。有一些测试库专门设计成允许在与源文件相同的文件中编写测试,以帮助你完成这一工作,比如doctest。
但我不太确定这些在规模上是否是好主意,因为我没有大型项目的经验。
英文:
> My solution is to introduce a variable in CMAKE "build_tests" and if it is ON, then symbols are not hidden.
The disadvantage of this is that for a single generated buildsystem, you basically have to choose between what you'd like to see for release (with symobls visible on an as-needed basis) and what is usable to you for testing. You'd have to generate a buildsystem for testing, and one for release (if I understand correctly), which doesn't seem ideal. The same non-idealness would be the case if you created a visibility macro that only makes something visible for test builds.
It was brought up in comments that some people don't see a need to test internals- and that only things that are part of the interface of a library need to / should be tested. I think it can be useful to test lower-level parts of a library that aren't part of the library's interface.
The thing is, for a single shared library target in a single generated buildsystem, each symbol will either be visible or it won't. So if you want to just have a single generated buildsystem where you build your shared library without compromising on what you make visible from it, but also be able to test its internals, you either need to change the way you build the shared library, or change the way you build your test executables.
What's practical will depend on the characteristics of your library and the internals you want to test.
You could try to refactor your inner logic into its own static library, and have your shared library wrap the static library. Then the shared library would be what the users of your project use, and the static library would be what you use for testing, where you're not inhibited by any visibility mechanisms. The binary you'd be testing would be a test executable linked to a static library, and so there would probably be differences in things like how certain things get translated to machine code if you build with any optimizations. Just be aware of that. This approach might take more programming work to do the separation and wrapping.
Another thing you could consider is to just add specific source files implementing that internal logic of the library into a dedicated test executable (or multiple test executables for various source files). This could be pragmatic if the source files for your internals are pretty self contained from each other. If you don't use link-time optimizations and you want to do things really granularly, with many test executables- for example- one for each source file, this could end up with more compilation time than the previously mentioned approach with a static library- again, the details of the difference will depend on the characteristics of your project. There are testing libraries that are designed with enabling writing tests in the same file as sources to help you with this, such as doctest.
I'm not very sure if these are good ideas at scale though, since I don't have experience with large projects.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论