3

I am aware that there is a popular notion that implementation details do not warrant testing and/or that the implementation should just be made public. For the sake of this question, please assume that there is merit to both testing and to minimising the API.

How should one go about testing an internal free function?

// Example for C
static MyType do_something_internal(OtherType * X);
// Or (roughly) equivalently
namespace { MyType do_something_internal(OtherType * X); }

The option we currently take is to expose the call, but in a detail namespace:

// Preserve the friendly function name and internal linkage
static MyType do_something_internal(OtherType * X)
extern MyType detail_do_something_internal(OtherType *X)
  { return do_something_internal(OtherType *X); }

// C++ version preserving the external linkage of the original
namespace detail {MyType do_something_internal(OtherType * X);}

We could instead expose a single object containing references to the internal functions:

struct exposed_for_test {
  auto detail_do_something_internal = do_something_internal;
  auto detail_other = other;
} // Similar in C, but somewhat less convenient to write out

We could wrap the testing interface in preprocessor macros with varying degrees of care:

#ifndef TESTING_BUILD
#define LINKAGE extern
#else
#define LINKAGE static
#endif
LINKAGE MyType do_something_internal(OtherType * X)
// or:
#ifdef TESTING_BUILD
static MyType do_something_internal(OtherType * X);
extern MyType detail_do_something_internal(OtherType * X)
{ return MyType do_something_internal(OtherType * X); }
#endif

At present I am leaning towards the last option. Any non-test code that calls the exposed interface will fail to link in the release build. However, this seems to unavoidably double compile time, and it feels a little uncomfortable to test different object files to those released. Perhaps it would be better to leave the testing interface in place and rely on link time optimisation to strip it out.

I am also considering writing the test code inline with the release code to access static functions directly. This clutters up the "real code" with "test code" though, and is probably a contentious solution.

All of the above options seem pretty crude however, so I'd like to ask the community - do you have any better ideas, or do you favour one of the above? Thank you.

Jon Chesterfield
  • 2,251
  • 1
  • 20
  • 30
  • I doubt I'm the only person working on projects written in both. I expect the optimal solution to be different in each case - "detail namespaces" are certainly a pain in C - and it doesn't seem worth posting the exact same question twice. – Jon Chesterfield May 02 '15 at 12:12
  • 2
    You may consider an opaque type hiding all details and make it visible for testing. –  May 02 '15 at 12:28
  • in general, the preferred technique is to hide the data and the implementation (see C++ for excellent examples) Then use 'accessor' functions to get/set the data. Then, the data representation and the implementation can be changed without changing anything else. – user3629249 May 02 '15 at 17:18
  • 1
    I really like your question and specifically the suggestion to great a struct named exposed_for_test(ing). At least you know people wouldn't think they are supposed to be using that one. – AturSams Sep 29 '15 at 15:04

2 Answers2

1

If you are willing to go as far as "inlining" the test code What about including the *.cc file in the *_test.cc file? In my humble opinion, often times helper functions can make a home in another file where they are public utility functions. Otherwise, they are too small and insignificant for testing or their role is not well defined. Yeah, it's entirely possible that no one else will ever use that other *_helper_util.h interface but that is true for a lot of code. Right?

AturSams
  • 7,568
  • 18
  • 64
  • 98
  • Thanks. I think this is pretty close to what I'm currently doing. File foo.c gets a foo.h and a foo_impl.h. I'm also considering making all implementation functions static, in which case #including foo.c in foo_test.c doesn't trouble the linker. – Jon Chesterfield Sep 29 '15 at 16:36
  • @JonChesterfield Would putting the implementation in an unnamed namespace serve the same purpose (as making the function static)? – AturSams Sep 30 '15 at 08:23
  • @ zehelvion Approximately. You would get a different mangled symbol name. I thought you would also get external symbol linkage, but it seems C++11 changed this to internal [linkage](http://stackoverflow.com/questions/4181059/linkage-of-symbols-within-anonymous-namespace-within-a-regular-namespace). I expected to be able to provoke multiply defined symbol errors with namespace {}, but haven't been able to in g++ or clang++. Thanks. I can't quite bring myself to go and read the ODR in the standard right now though :( – Jon Chesterfield Sep 30 '15 at 09:01
  • Not sure if this helps (probably not) but iirc one way to upset c++ is to declare the function inside the unnamed namespace and define it in outside that unnamed namespace in the same c file. – AturSams Sep 30 '15 at 11:59
-2

this link: https://ved789.wordpress.com/2009/11/27/coupling-cohesion-encapsulation-polymorphism/

discusses Coupling, Cohesion, Encapsulation, Polymorphism.

for the purposes of this question, 'coupling' is the main interest:

Here is what the link has to say about 'coupling'

(not especially the first sentence.)

Coupling is the unnecessary dependance of one component upon another component’s implementation.

this link: http://searchsqlserver.techtarget.com/definition/data-hiding

defines data hiding as:

Data hiding is a characteristic of object-oriented programming. Because an object can only be associated with data in predefined classes or templates, the object can only "know" about the data it needs to know about. There is no possibility that someone maintaining the code may inadvertently point to or otherwise access the wrong data unintentionally. Thus, all data not required by an object can be said to be "hidden."

What all above means is that the data/implementation should be hidden. In large projects, this becomes a major concern.

user3629249
  • 16,402
  • 1
  • 16
  • 17
  • what the ^&*()_)(*&?? I did not write this and I certainly did not write this in back in May 2 of this year for a question only ask a few hours ago. – user3629249 Sep 30 '15 at 02:27