Testing with GTest and GMock: shared vs. static libraries
Asked Answered
T

2

6

I think this question may violate some of the Q&A standards for the site, as the answer(s) I may receive could be regarded as opinion-driven. Nevertheless, here it goes...

Suppose we're working on a C++ project, using CMake to drive the build/testing/packaging process, and GTest and GMock for testing. Further suppose the structure of our project looks like this:

cool_project
|
|-- source
| |
| |-- module_foo
| | |
| | |-- (bunch of source files)
| |
| |-- module_bar
| |
| |-- (yet more source files)
|
|-- tests
|
|-- module_foo
| |
| |-- (tests for module_foo)
|
|-- module_bar
|
|-- (tests for module_bar)

This is, of course, an oversimplified situation, but you get the idea.

Now well, if these modules are libraries and every test (i.e. every directory under tests) is an executable, we need to link the latter with the former. The thing is, if these libraries are shared, the loader needs of course to find them. An obvious solution is to set the test's working directory to the library's directory, using CMake's set_property. However, if both GTest and GMock were also built as shared libraries, this won't work as they need to be also loaded.

The solutions I came up with were:

  • Copy both libraries (i.e. GTest and GMock) to the module's build directory. This feels kind of stupid as the main benefit of shared libraries (i.e. to share code among programs) gets completely bypassed and we end up with several copies of these all over the build directory.
  • Build both GTest and GMock as static libraries instead. This means that we now end up with a copy of both libraries into every executable, which increases its size. Even though we don't have 1000 tests, this feels somehow awkward.

So, given this situation, I would like to know if anyone has ever been struck with it, and what path did he/she take. (If the solution was other than the ones I mentioned, I would be happy to hear all about it.) Ideally, I'd like to be in a position in which I could make && make test and have all the tests run, without having to run any extra script to accommodate things. Having all libraries built as static libraries does the job, but what if I'm building them as shared libraries instead? Must I build them twice? That's silly.

The other problem also runs along these lines, but I think its solution involves a redesign or a similar artifact. Let's suppose module_foo depends on a third-party library, e.g. library_baz. If module_foo links directly to library_baz, then any test on the former would need to load library_baz, even though it may be testing an unrelated functionality. Same issue arises.

Mocking seems like the right thing to do here, but somehow I feel it doesn't make much sense to refactor module_foo in order for it to talk to an interface (be it by virtue of either dynamic or static polymorphism) as it doesn't need such flexibility: library_baz does the job. I suppose some people would say something like 'Sure, you don't need the flexibility today, but who knows tomorrow?'. That seems counter-intuitive to me, trying to preview all possible scenarios a system may run into, but then again, there are people out there with far more experience than me.

Any thoughts?

Tennies answered 20/1, 2014 at 15:6 Comment(2)
Would you consider installing gtest and gmock as local shared libraries on the system, i.e. in /usr/local/lib? Dynamic linkage will find them there if your ldconfig search paths are normal and then you can just add -lgtest -lgmock -pthread to the linkage options of your tests. Is there some reason that wouldn't work for your test setup?Dockyard
First of all, sorry for the (very) late reply. The reason this setup wouldn't be the perfect solution to this problem is that, ideally, I would like to deploy the tests along with the application, and I don't want to force users to install things some of they may not know about.Tennies
T
2

It seems I was trying to kill a mosquito by using a nuclear missile.

The solution I came up with was to simply build all libraries as static objects when testing. True, I end up with pretty big binaries, but it's not the case that I'll be distributing those.

So, to summarize:

  • Both GTest and GMock are built as static libraries.
  • Same goes for the libraries that contain the functionality I'm testing.
  • Tests then link against those, and thus can be run without messing with the working directory at all.

There are no significant drawbacks to this setup. Whenever I want to give the entire system a try, I simply switch to shared libraries.

Tennies answered 12/3, 2014 at 15:5 Comment(0)
C
0

That way I see this done (at least on Windows, I don't develop on *nix) is quite independent of any testing:

Simply all binary build artifacts and dependencies that are required to run have to be copied (or directly created in) into a ./bin directory.

Then you can execute any executable from this ./bin directory and all shared libraries are in place there.

Charger answered 12/2, 2014 at 7:32 Comment(5)
Makes sense, but I think building a project with testing enabled and following your advice will then produce a large amount of binaries within the bin directory, since I'm using CTest and each test is basically an executable. Thanks anyway for your answer.Tennies
@faranwath - Note that I do not suggest any global /bin directory. Just ..../my_project_dir/bin. That should be sufficient, shouldn't it?Charger
Absolutely, I just wanted to see if anyone knew how to avoid doing this, but my hope was based purely on personal and not practical matters. As I said, your answer does qualify as helpful.Tennies
@faranwath - What is the problem with having many test executable inside a single directory?Charger
I'm not entirely sure why we keep discussing this, but here it goes: there's absolutely no problem with this, and in fact it might be the solution I choose in the future. I just wanted to place each test executable inside the directory associated with the module it is testing. Given that I'm organizing them following the structure of the modules, the symmetry-loving part of my brain thought that would be a great idea.Tennies

© 2022 - 2024 — McMap. All rights reserved.