by Filip Sajdak
but our code is untestable
is the degree to which a software artifact supports testing in a given test context.
if is high, then finding faults in the system by means of testing is easier.
is not an intrinsic property of a software artifact and can not be measured directly
is an extrinsic property which results from interdependency of the software to be tested and the test goals, test methods used, and test resources
Single responsibility principle
Open/closed principle
Liskov substitution principle
Interface segregation principle
Dependency inversion principle
Single responsibility principle
Open/closed principle
Liskov substitution principle
Interface segregation principle
Dependency inversion principle
double pow(double a, int b) {
if (b<0)
return pow(1/a, -b);
double r = 1;
for (auto i = 0; i < b; ++i)
r *= a;
return r;
}
// ...
pow(5,3); // returns 125
pow(10, -3); // returns 0.001void print(double d)
{
std::cout << "<double>" << d << "</double>";
}
// ...
print(3.14);
void print(std::ostream& out, double d)
{
out << "<double>" << d << "</double>";
}
// ...
print(std::cout, 3.14);void print(std::ostream& out, double d, const Formater& f)
{
out << f(d);
}
// ...
print(std::cout, 3.14, XmlFormater{});What design decision I have just made?
template <typename Formater>
void print(std::ostream& out, double d, Formater f)
{
out << f(d);
}
// ...
print(std::cout, 3.14, XmlFormater{});std::string formater(double d);
void print(std::ostream& out, double d)
{
out << formater(d);
}
// ...
print(std::cout, 3.14);void print(std::ostream& out, double d,
const Formater& f)
{
out << f(d);
}template <typename Formater>
void print(std::ostream& out, double d, Formater f)
{
out << f(d);
}void print(std::ostream& out, double d)
{
out << formater(d);
}run-time
compile-time
link-time
void print(std::ostream& out, double d, const Formater& f)
{
out << f(d);
}
void print(double d)
{
print(std::cout, d, XmlFormater{});
}
// ...
print(3.14);class Operation
{
public:
double operator()(double x, double y)
{
return 3*x + y;
}
};
class Operation
{
public:
Operation(double c) : c{c} {}
double operator()(double x, double y)
{
return c*x + y;
}
private:
double c;
};
class X
{
public:
void update()
{
auto state = d.getState();
s.save(state);
}
private:
Device d;
Store s;
};
class X
{
public:
X(std::shared_ptr<Device> d, std::shared_ptr<Store> s)
: d{d}, s{s} {}
void update()
{
auto state = d->getState();
s->save(state);
}
private:
std::shared_ptr<Device> d;
std::shared_ptr<Store> s;
};
// in cpp file
namespace {
DeviceMock* ptr = nullptr;
}
DeviceMock::DeviceMock() {
assert(ptr == nullptr);
ptr = this;
}
DeviceMock::~DeviceMock() {
ptr = nullptr;
}
State Device::getState() {
return ptr->getState();
}
class X
{
public:
void update()
{
auto st = d.getState();
s.save(st);
}
private:
Device d;
Store s;
};
struct DeviceMock {
DeviceMock();
~DeviceMock();
MOCK_METHOD0(getState, State());
};template <typename DeviceType, typename StoreType>
class X {
public:
X(DeviceType& d, StoreType& s) : d{d}, s{s} {}
void update() {
auto state = d.getState();
s.save(state);
}
private:
DeviceType& d;
StoreType& s;
};
template <typename DeviceType, typename StoreType>
class X {
public:
void update() {
auto state = d.getState();
s.save(state);
}
DeviceType& getDevice() { return d; }
StoreType& getStore() { return s; }
private:
DeviceType d;
StoreType s;
};
class X {
public:
template <typename T>
X(T d) : model_d(new model<T>(std::move(d))) {}
void update() { model_d->getState(); }
private:
struct imodel {
virtual ~imodel() = default;
virtual State getState() = 0;
};
template <typename T>
struct model : imodel {
T d;
model(T d) : d{std::move(d)) {}
State getState() { return d.getState(); }
};
std::unique_ptr<imodel> model_d;
};
No - we should change a code to improve its design.
Good design is testable, and design that isn’t testable is bad.
Michael Feathers
Working Effectively with Legacy Code
std::ostringstream local_stream;
auto prev_buf = std::cout.rdbuf();
std::cout.rdbuf(local_stream.rdbuf());
std::cout << "this will be written to local_stream instead of standard output";
std::cout.rdbuf(prev_buf);template <typename DeviceType, typename StoreType>
class X {
public:
void update() {
auto state = d.getState();
s.save(state);
}
template <typename = decltype(d.gmock_getState())>
DeviceType& getDevice() { return d; }
template <typename = decltype(s.gmock_save(d.getState()))>
StoreType& getStore() { return s; }
private:
DeviceType d;
StoreType s;
};