I previously asked this question under another name but deleted it because I didn't explain it very well.
Let's say I have a class which manages a file. Let's say that this class treats the file as having a specific file format, and contains methods to perform operations on this file:
class Foo {
std::wstring fileName_;
public:
Foo(const std::wstring& fileName) : fileName_(fileName)
{
//Construct a Foo here.
};
int getChecksum()
{
//Open the file and read some part of it
//Long method to figure out what checksum it is.
//Return the checksum.
}
};
Let's say I'd like to be able to unit test the part of this class that calculates the checksum. Unit testing the parts of the class that load in the file and such is impractical, because to test every part of the getChecksum()
method I might need to construct 40 or 50 files!
Now lets say I'd like to reuse the checksum method elsewhere in the class. I extract the method so that it now looks like this:
class Foo {
std::wstring fileName_;
static int calculateChecksum(const std::vector<unsigned char> &fileBytes)
{
//Long method to figure out what checksum it is.
}
public:
Foo(const std::wstring& fileName) : fileName_(fileName)
{
//Construct a Foo here.
};
int getChecksum()
{
//Open the file and read some part of it
return calculateChecksum( something );
}
void modifyThisFileSomehow()
{
//Perform modification
int newChecksum = calculateChecksum( something );
//Apply the newChecksum to the file
}
};
Now I'd like to unit test the calculateChecksum()
method because it's easy to test and complicated, and I don't care about unit testing getChecksum()
because it's simple and very difficult to test. But I can't test calculateChecksum()
directly because it is private
.
Does anyone know of a solution to this problem?
One way would be to extract the checksum method out into its own class and have a public interface with which to test.
Basically, it sounds like you want a mock to make unit testing more feasible. The way you make a class targetable for unit testing independantly of the object hierarchy and of external dependencies is through dependency injection. Create a class "FooFileReader" like so:
class FooFileReader
{
public:
virtual std::ostream& GetFileStream() = 0;
};
Make two implementations, one that opens a file and exposes it as a stream (or an array of bytes if that is what you really need.) The other is a mock object that just returns test data designed to stress your algorithm.
Now, make the foo constructor have this signature:
Foo(FooFileReader* pReader)
Now, you can construct foo for unit testing by passing a mock object, or construct it with a real file using the implementation that opens the file. Wrap construction of the "real" Foo in a factory to make it simpler for clients to get the correct implementation.
By using this approach, there's no reason not to test against " int getChecksum()" since its implementation will now use the mock object.
The simple, direct answer is to make your unit-test class a friend of the class under test. This way, the unit test class can access calculateChecksum()
even though it's private.
Another possibility to look at is that Foo appears to have a number of unrelated responsibilities, and might be due for re-factoring. Quite possibly, computing a check sum shouldn't really be part of Foo
at all. Instead, calculating a checksum might be better off as a general purpose algorithm that anybody can apply as needed (or possibly, kind of the reverse -- a functor to use with another algorithm like std::accumulate
).
I'd start by extracting the checksum-calculation code into it's own class:
class CheckSumCalculator {
std::wstring fileName_;
public:
CheckSumCalculator(const std::wstring& fileName) : fileName_(fileName)
{
};
int doCalculation()
{
// Complex logic to calculate a checksum
}
};
This makes it very easy to test the check-sum calculation in isolation. You could however take it one step further and create a simple interface:
class FileCalculator {
public:
virtual int doCalculation() =0;
};
And the implementation:
class CheckSumCalculator : public FileCalculator {
std::wstring fileName_;
public:
CheckSumCalculator(const std::wstring& fileName) : fileName_(fileName)
{
};
virtual int doCalculation()
{
// Complex logic to calculate a checksum
}
};
And then pass the FileCalculator
interface to your Foo
constructor:
class Foo {
std::wstring fileName_;
FileCalculator& fileCalc_;
public:
Foo(const std::wstring& fileName, FileCalculator& fileCalc) :
fileName_(fileName),
fileCalc_(fileCalc)
{
//Construct a Foo here.
};
int getChecksum()
{
//Open the file and read some part of it
return fileCalc_.doCalculation( something );
}
void modifyThisFileSomehow()
{
//Perform modification
int newChecksum = fileCalc_.doCalculation( something );
//Apply the newChecksum to the file
}
};
In your real, production code you would create a CheckSumCalculator
and pass it to Foo
, but in your Unit Test code you could create a Fake_CheckSumCalculator
(that, for example always returned a known predefined checksum).
Now, even though Foo
has a dependency on CheckSumCalculator
, you can construct and Unit Test these two classes in complete isolation.
#ifdef TEST
#define private public
#endif
// access whatever you'd like to test here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With