Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ and Dependency Injection in unit testing

Suppose I have a C++ class like so:

class A
{
    public:
        A()
        {

        }

        void SetNewB( const B& _b ) { m_B = _b; }

    private:
        B m_B;
}

In order to unit test something like this, I would have to break A's dependency on B. Since class A holds onto an actual object and not a pointer, I would have to refactor this code to take a pointer. Additionally, I would need to create a parent interface class for B so I can pass in my own fake of B when I test SetNewB.

In this case, doesn't unit testing with dependency injection further complicate the existing code? If I make B a pointer, I'm now introducing heap allocation, and some piece of code is now responsible for cleaning it up (unless I use ref counted pointers). Additionally, if B is a rather trivial class with only a couple of member variables and functions, why introduce a whole new interface for it instead of just testing with an instance of B?

I suppose you could make the argument that it would be easier to refactor A by using an interface. But are there some cases where two classes might need to be tightly coupled?

like image 856
lhumongous Avatar asked Apr 09 '10 18:04

lhumongous


2 Answers

I think you're taking the idea of Unit Testing too far. In this case, A and B are one unit, i.e., A can't exist without B. First, test B and make sure it passes all of the B-specific unit tests, then once that passes, test A and make sure it behave how it's supposed to.

like image 100
miked Avatar answered Nov 02 '22 02:11

miked


If B is really a dependency injected into A then you should consider a number of other options. First is injecting B at construction time:

class A
{
    public:
        A( const B& _b ) : m_B(_b) {}

    private:
        const B& m_B;
};

If you really want to modify B during the lifecycle of your A object, then ask yourself if you're really the owner of B. If not, pass a pointer to it and assume the one who passes it is responsible for its lifecycle -- this is a tricky route though, and might require you to use ref-counted pointers.

If what you want to do is take a copy of B for yourself then you could define a clone() method on B. If you want to create your own object using information contained in B then you can inject a MyBFactory in the constructor and use it with this B.

like image 34
Philippe Beaudoin Avatar answered Nov 02 '22 03:11

Philippe Beaudoin