模对象引入了一种很好的方法来对某些程序单元进行深度行为测试。 您只需将模拟的依赖关系传递给测试过的单元,并检查它是否适用于依赖关系。可维对象
让你有2类A和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();
}
}
}
想象类B被与单元测试TESTB测试。我们可以将B传递给它的构造函数(执行基于状态的测试)或者将B的模拟传给它(做基于行为的测试)。假设我们已经选择了第二种方法(例如,我们不能直接验证A的状态,并且可以间接地执行它),并且创建了单元测试TestA(它不包含任何对B的引用)。
因此,我们将推出一个接口IDependency和类的样子:
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();
}
}
}
和单元测试TESTB是类似于:
[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);
}
}
确定。对开发人员来说,这是一个快乐的时刻 - 所有测试都经过测试,所有测试都是绿色的一切都很好。
但是!如果某人修改了B类的逻辑(例如将if(State!=某个特殊值)修改为if(State!=另一个值)),则只有TestB失败。
这家伙修复了这个测试,并认为一切顺利了。
但是,如果您尝试将B传递给A的构造函数,那么DoSomething将会失败。
它的根本原因是我们的模拟对象。它修复了B对象的旧行为。当B改变了行为时,模拟没有反映出来。
那么,我的问题是如何让B的模拟跟着B的行为变化?
你的答案主要是我想的和我想要澄清的东西。看来你建议编写一套测试来验证Mocks实现的接口是否是例外的。这是一个好习惯吗?它是否引入了一些维护问题?看起来我们应该单元测试测试工具类呢?我们不应该在模拟中修复行为,而应该在单元测试中修复它(可能是每个接口实现应该被覆盖的抽象基本单元测试类,即所谓的合同测试)。我对吗? – gerichhome 2013-02-14 16:17:30