Mock objects introduce a good approach to do deep behavior testing of some program unit. You just should pass mocked dependency to the tested unit and check if it works with dependency as it should do.
Let you have 2 classes A and B:
public class A
{
private B b;
public A(B b)
{
this.b = b;
}
public void DoSomething()
{
b.PerformSomeAction();
if(b.State == some special value)
{
b.PerformAnotherAction();
}
}
}
public class B
{
public BState State { get; private set; }
public void PerformSomeAction()
{
//some actions
State = some special value;
}
public void PerformAnotherAction()
{
if(State != some special value)
{
fail(); //for example throw new InvalidOperationException();
}
}
}
Imagine class B is being tested with unit test TestB.
To unit test class A we can either pass B to it's constructor (to do state based testing) or pass B's mock to it (to do behavior based testing).
Let say we have chosen the second approach (for example we can't verify A's state directly and can do it indirectly) and created unit test TestA (which doesn't contain any reference to B).
So we will introduce an interface IDependency and classes will look like:
public interface IDependency
{
void PerformSomeAction();
void PerformAnotherAction();
}
public class A
{
private IDependency d;
public A(IDependency d)
{
this.d = d;
}
public void DoSomething()
{
d.PerformSomeAction();
if(d.State == some special value)
{
d.PerformAnotherAction();
}
}
}
public class B : IDependency
{
public BState State { get; private set; }
public void PerformSomeAction()
{
//some actions
State = some special value;
}
public void PerformAnotherAction()
{
if(State != some special value)
{
fail(); //for example throw new InvalidOperationException();
}
}
}
and unit test TestB is something similar to:
[TestClass]
public class TestB
{
[TestMethod]
public void ShouldPerformAnotherActionWhenDependencyReturnsSomeSpecialValue()
{
var d = CreateDependencyMockSuchThatItReturnsSomeSpecialValue();
var a = CreateA(d.Object);
a.DoSomething();
AssertSomeActionWasPerformedForDependency(d);
}
[TestMethod]
public void ShouldNotPerformAnotherActionWhenDependencyReturnsSomeNormalValue()
{
var d = CreateDependencyMockSuchThatItReturnsSomeNormalValue();
var a = CreateA(d.Object);
a.DoSomething();
AssertSomeActionWasNotPerformedForDependency(d);
}
}
Ok. It's a happy moment for developer - everything is tested and all tests are green. Everything is good.
But!
When someone modifies logic of class B (for example modifies if(State != some special value) to if(State != another value) ) only TestB fails.
This guy fixes this test and thinks that everything goes well again.
But if you try to pass B to constructor of A A.DoSomething will fail.
The root cause of it is our mock object. It fixed old behavior of B object. When B changed its behavior mock didn't reflect it.
So, my question is how to make mock of B follow changes of behavior of B?