0

I have written a Command Line Interface, where the user has to construct an object basically by providing input to a bunch of questions. I have a hard time testing these functions as there is too much happening in there. Basically for every input, there is some validation and it will loop forever, print error message, asks again until the user enters a correct input.

A quite simplified case might look something like this:

// CommandLineInterface.h
void createPerson(DatabaseClass database, std::ostream ostream, std::istream istream)

// CommandLineInterface.cpp
namespace {
std::string getPersonNameInput(std::ostream ostream, std::istream istream) {
   while(true) {
     ostream << "Enter Person Name";
     std::string name;
     istream >> name; 

     if(someOtherFunctionToValidateName(name)) 
        return name; 

     ostream << "Some error message";
   }
}
}

void createPerson(DatabaseClass database, std::ostream ostream, std::istream istream) {
    auto name = getPersonNameInput(ostream, istream); 
    auto age = getPersonAgeInput(); 
    database.addPerson(Person { name, age }); 
}

So there is one function part of the public interface, which delegates input, error handling, validation to some helper function in an anonymous namespace.

I've learnt that you shouldn't test Implementation Details (such as functions in an anonymous namespace or private functions), but only test the public Interface, which will call these directly. But I also learnt to test only one noticeable end result per unit (the big end result here is the successful call of some function with the constructed object ... but there are loads of other noticeable results such as the error messages). This might be an indicator that my function does to many things and does not separate concerns.

One "fix" would be to put getPersonNameInput in the header as well and make it part of the public interface and then unit test separately. I could then test createPerson by mocking this function. But that seems wrong to me as well. Making helper functions part of the public interface.

Is my design just bad here? If yes what would be ways to do improve the design, separate concerns, make it more testable? If no how would I test it then best? (Btw: I now that it's sometimes possible to test private functions or function in anonymous namespaces, but as said above you usually would not want to test these)

Thanks for help!

1 Answer 1

3

"you shouldn't test Implementation Details (such as functions in an anonymous namespace or private functions)" - this philosophy is controversial, or at least debated, or, at a minimum, I disagree with it. It is totally possible and usable and frequently desirable to test implementation "details". In fact, once you've tested them, frequently you've very little left to do to test the mainline. As in this case.

Perhaps the problem is you have an over-inclusive idea of what is an "implementation detail". Hint: If you think the entire crucial logic and calculation of your program is an implementation detail then you're either working on an extremely trivial program, or, more likely, your conception of "detail" is way too large.

Or perhaps you need more clarity on what the "public" interface is. It's easy to believe the "public" interface of a command-line program is ... well, the command line. But not so! There are multiple uses of the concept "public interface" and for design/implement/test purposes the "public interface" is a concept relating to the structure of the program - not the UI (the command line, in this case).

Seen that way you might, for example, decide that your program's structure is inappropriate. Perhaps there should be free functions - or even a class - that represent operations to be performed by the program, and those are properly designed according to "software engineering" guidelines - clear specifications, separation of concerns, etc. At the level of the programming language, the structure of your program, those would be "public" in the sense you could declare them and call them from any file. And so they'd be appropriate to test and you could test them. But from the point-of-view of the user interface they would of course be private - because the user wouldn't see them or invoke them directly from the command line.

3
  • That's a point of view I had as well (I mean I could put a whole Library in an anonymous namespace that wouldn't make them an Implementation Detail) and I find your arguments quite convincing! What exactly is it that make you believe this should be part of the public interface / is not an implementation detail? Would you then move (some) of the functions outside the namespace (and declare them in a header?), because testing free functions is hard -> testing free functions in anonymous namespace harder -> mocking free functions in anonymous namespace is really hard -> loads of tricks needed
    – Leon0402
    Jan 11, 2021 at 17:55
  • I don't see why getPersonNameInput needs to be in an anonymous namespace. Why not use two headers for the '"command line interface" concept - it's frequently done. cli.h looks just as it is now, that's the "public" interface used by, e.g., your main(). Then there's a cli-impl.h where the first line is #include <cli.h> and that's followed by the decls of everything else - that is, everything that needs testing. cli-impl.h is included by unit tests, cli.h by your mainline and also by any regression or end-to-end tests, if you have them (speaking generally, not for this case).
    – davidbak
    Jan 11, 2021 at 18:01
  • Thanks for the suggestion, I like the idea!
    – Leon0402
    Jan 11, 2021 at 18:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.