views:

318

answers:

6

Hello,

I'm using C++Test from Parasoft for unit testing C++ code. I came across the following problem. I have a function similar to the next one (pseudocode):

bool LoadFileToMem(const std::string& rStrFileName)
{
    if( openfile(rStrFileName) == successfull )
    {
         if( get_file_size() == successfull )
         {
            if( read_entire_file_to_buffer() == successfull )
            {
                return true;
            }
            return false;
         }
         return false;
    }
    return false;
}

My questions in this case are:

Should I use stubs for file system functions? Or should I include specific sample test files for running the unit tests?

In my case std::fstream class is used for file input.

Has anyone better suggestions? (Best if done in C++Test but not mandatory)

Thank you,

Iulian

+4  A: 

I would go for short sample test files. They can be checked into source control along with the test code. The reason I would do it is that the intent of your function is to load a file, so this is what you should be testing.

quant_dev
Yes, but maybe we want to "simulate" some errors that do not occur always when using files. I guess that sometimes some errors are impossible to simulate with a *real* filesystem.
Iulian Şerbănoiu
If a file reader error is impossible to simulate with a real filesystem, then either it is very obscure or simply won't happen, since you will be running your app on a *real* filesystem. The exception is if you're developing on system X and want to test for errors specific for system Y. All things considered, I would not *rule out* mocking the file in the unit tests, but would start from tests using real files.
quant_dev
+2  A: 

My suggestion:

Create stubs for functions that will call this function.

Create a unit test for this specific function, with sample test files.

Create a integration test with no stubs to test the whole process.

Samuel Carrijo
A: 

It is generally a good idea in unit testing that you want to mimic the original functionality as close as possible while covering as much of the code you are testing (code coverage). Stubs aside, which I am not familiar with, an example would be that you want to make sure that, provided the wrong filename is given, etc line 3 in your file:

if( openfile(rStrFileName) == successfull )

then you will accurately handle this situation and return false. If this can be done by stubs then so be it. In my case I would use a sample test file and store that with my tests. This is a common practice.

Remember that the whole point is to test out the function as close to reality as you can. This will ensure a lot of strange cases that you can't anticipate will be caught automatically so you can fix it, which is the point of unit testing to begin with.

BuckFilledPlatypus
A: 

I think your looking for a technique called fault injection. I saw a project several years ago that would cause programs to go into rarely tested error conditions (file permission errors, malloc returning 0, etc.) .. I just can't remember the name of it. Hopefully the wikipedia link could get you started.

eduffy
It's a start. Hope I'll manage to do this in C++Test
Iulian Şerbănoiu
+3  A: 

For unit-testing THIS function, you should use stubs for each of the called functions.

Each called function then has its own unit test suite, which exercises that function.

For read_entire_file_to_buffer(), you want at least one test file that overflows the buffer, massively, to verify that you do not crash and burn when they feed you the New York Stock Exchange histories instead of the 40-character config file you were expecting.

You are correct, but the buffer is dynamically allocated to be equal to the size of the file. I simplified the problem a little [I have stubs for allocating memory - but C++Test doesn't help me making stubs for fstream class methods].
Iulian Şerbănoiu
@ Iulian: The dynamical allocation may still fail (eg. if you request 100 GB of memory), if you're interacting with data you do not control, you should test how your code handles the data it cannot handle (does it fail gracefully or just crashes?).
quant_dev
@John R Strohm: This is correct in theory, but in many cases an overkill. I don't want to maintain 1000 lines of production code + 20 000 lines of testing code, with its own set of problems and bugs. Shall I write tests for the test code? Mocking a complex function is not an easy task.
quant_dev
I have tests for dynamic allocation - fail/(not fail) which forces the test to fail in case something is wrong with the code.[leave this one out please :) - it is handled]
Iulian Şerbănoiu
@quant_dev: If you are maintaining a monster test suite for some piece of code, it usually indicates serious problems with the overall design of the code.The old principle from Automated Test Equipment was that a well-designed Unit Under Test generally only required very simple test gear. If the test set was HUGE compared to the UUT, it was because the UUT was poorly-designed. (Yes, I've seen this in practice.)
A: 

Honestly, I would split this function into two. One function would read from a std::istream, and one would open the file and return an ifstream (possibly a heap-allocated one by a smart pointer). Then, you can easily unit-test the first by supplying an istringstream instead of an ifstream, and the latter should be easy-to-test too.

rlbond