In the process of refactoring non-testable code we are re-designing some of ours classes so that it can be easily replaced by a stub or mock during unit tests.
Here is an example of such class (implementation is omitted for simplification):
class LightController {
public:
void turnOn();
private:
CanBus m_canBus;
};
The turnOn()
method uses internally the CanBus
which itself deals with hardware communication (implementation is omitted for simplification):
class CanBus {
public:
void write(std::array<unsigned char, 8> data);
};
We can't test LightController
as is because CanBus
won't work without proper hardware environment. The refactoring is planned as follow:
- Pass The
CanBus
by dependency injection to theLightController
- Create a stub class with no hardware dependency to replace
CanBus
during tests
About the second steps, we can either make functions virtual and create a derived class:
class CanBus {
public:
virtual void write(std::array<unsigned char, 8> data);
};
class CanBusStub : public CanBus {
public:
void write(std::array<unsigned char, 8> data) override;
};
Alternatively, we can suggest the creation of a proper interface class and make both CanBus
and CanBusStub
derive from it:
class CanBusInterface {
public:
virtual void write(std::array<unsigned char, 8> data) = 0;
};
class CanBus : public CanBusInterface {
public:
void write(std::array<unsigned char, 8> data) override;
};
class CanBusStub : public CanBusInterface {
public:
void write(std::array<unsigned char, 8> data) override;
};
I know this is probably an opinion-based question which is extremely dependent on the context, but given our team is fairly new with unit tests and that none of our codebase is based on Interface, would you suggest to go the simpler way (just makes CanBus
methods virtual
) or the complete way (enforcing creation of interfaces)?
Is there any important downside with making the CanBus
methods virtual?