Liskov Substitution Principle - SOLID
In 1988 Barbara Liskov wrote something that now stands for L in SOLID principles. Let's dive in and learn what is it and how does it relate to TDD.
Here is the original formulation: "If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behaviour of P is unchanged when o1 is substituted for o2 then S is a subtype of T."
Simply speaking: "Derived class objects must be substitutable for the base class objects. That means objects of the derived class must behave in a manner consistent with the promises made in the base class contract."
Speaking even more simply: "Derived class objects should complement, not substitute base class behaviour."

LSP can also be described as a counter-example of Duck Test: "If it looks like a duck, quacks like a duck, but needs batteries – you probably have the wrong abstraction"
So, In Real World
If you have some class Foo and a derived class SubFoo, then if you change all the notions of Foo class to SubFoo – the program execution shouldn't change, as SubFoo doesn't change the Foo class functionality, and only extends it.
Let's See The Example
Getting back to ducks. Let's describe a Duck. We have very low expectations on it. We only expect it to be able to quack and nothing else.
describe('Duck', function(){
describe('#quack', function(){
it('produces "Quack" sound', function(){
const duck = new Duck();
expect(duck.quack()).toEqual('Quack');
});
});
});
Fine, now lets define the basic duck.
class Duck{
constructor(){
// Duck initialization process
}
quack(){
return 'Quack';
}
}
We run the spec and it passes. Cool, now let's create a derived class MechanicalDuck. It should also be able to quack. The only difference is that it needs batteries to operate.
class MechanicalDuck extends Duck{
constructor(battery=null){
super();
this._battery = battery;
}
quack(){
if(!this._battery){
throw 'Need battery to operate.';
}
return 'Quack';
}
}
Now according to LSP, we should be able to safely change instances of base class to instances of derived class. Let's change our spec a bit and try to use MechanicalDuck instead of Duck.
Uh-oh, test failed. MechanicalDuck needs battery to quack. So MechanicalDuck here is clearly not a duck. Even though it's interface might look similar, it's behaviour is totally different.
But What Would Be A Proper Subclass?
In our case it might be a FemaleDuck. Let's implement it.
class FemaleDuck extends Duck{
constructor(){
super();
// Initialization of female stuff
this._butt = new FemaleDuckButt();
}
layAnEgg(){
const egg = this._butt.layAnEgg();
return egg;
}
}
FemaleDuck will successfully pass the duck test, as we didn't change the behaviour, but only extended it. Our duck can lay eggs, hurray!