测试protected方法
现在有一个类,其中包含一个protected方法:
public class SomeClass { protected int SomeMethod(string arg) { ... } }如果我们需要对这个protected方法进行单元测试,可以在测试代码中准备一个辅助类型:
public class SomeClassForTest : SomeClass { public int PublicSomeMethod(string arg) { return this.SomeMethod(arg); } }于是在单元测试中,便可以通过调用PublicSomeMethod来测试基类的SomeMethod方法:
var testClass = new SomeClassForTest(); var result = testClass.PublicSomeMethod(null); Assert.Equal(0, result);非常简单。
如果您觉得麻烦,也可以将SomeClass类中的SomeMethod方法改为protected internal,这样便可以在InternalVisibleTo的测试程序集中使用了。不过,我觉得为单元测试而改变成员的访问级别不是一个合适的做法。
对protected方法进行Mock
现在有一个类,其中有一个protected方法:
public class SomeClass { protected virtual int SomeMethod(string arg) { ... } }并且,某个被测试的方法接受SomeClass作为参数。虽然被测试的方法不会直接调用SomeMethod方法,但是SomeMethod的实现会影响到公开接口的表现形式。于是,我们需要对SomeMethod进行Mock或Stub。为此,我们同样需要准备一个辅助类型:
public class MockSomeClass : SomeClass { protected override int SomeMethod(string arg) { return this.PublicSomeMethod(arg); } public virtual int PublicSomeMethod(string arg) { return base.SomeMethod(arg); } }在MockSomeClass中,我们覆盖了基类的SomeMethod实现,使它调用了子类中公开的PublicSomeMethod方法,而PublicSomeMethod内部又调用了基类的SomeMethod方法。因此,如果您不去进行任何处理,那么MockSomeClass会保持SomeMethod的实现不变。而如果您需要对SomeMethod进行Mock或Stub的时候,便可以从PublicSomeMethod下手:
Mock<MockSomeClass> mockSomeClass = new Mock<MockSomeClass>() { CallBase = true }; mockSomeClass.Setup(c => c.PublicSomeMethod("123")).Returns(123); DoSomeTest(mockSomeClass.Object); // use the mock object也很容易。
为了可测试性
值得注意的是,为了“可测试性”,第二部分中的protected方法必须是virtual的,因为我们需要在子类中进行override。同理,Mock框架能够辅助的方法也必须是virtual的,即使是一个public方法。那么,您觉得这是为了可测试性而做出的让步吗?或者换句话说,您觉得,一个不可以override的protected方法,但是会影响到其他公开接口的功能,这是不是一个合理的设计呢?如果这是一个合理的设计,又不想作出这样的让步……我们又该怎么做呢?