在 C# 中使用模拟和子类对受保护的方法进行单元测试



我正在尝试对公共类中的一些受保护方法进行单元测试。 我从这个类继承并通过从中返回超类的方法来测试继承的类。 我的示例代码如下:

基类:

public class A
{
// Private variables here
public A(){ }
protected virtual bool MethodOfA()
{
bool returnValue = false;
//some operation here to set returnValue
return returnValue;
}
protected bool AnotherMethodOfA()
{
bool anotherReturnValue = false;
bool operationCheck = MethodOfA();
if(operationCheck)
{
//do something to set the value of anotherReturnValue
}
return anotherReturnValue;
}
}

继承类:

public class B : A
{
// Private variables here
public B():base() { }
public new bool MethodOfA()
{
return base.MethodOfA();
}
public new bool AnotherMethodOfA()
{
var testMock = new Mock<A>();
//This is the part where I'm not sure how to get it to work.
testMock.CallBase = true; // I have tried it with and without this statement. couldn't get it to work
testMock.Protected()
.Setup<bool>("MethodOfA")
.Returns(true);
return base.AnotherMethodOfA();
}
}

测试:

public class TestB
{
private readonly B _sut
//some Mocks here for setup
public TestB()
{
_sut = new B();
}

[Fact]
public void AnotherMethodOfA_Test()
{
var result = _sut.AnotherMethodOfA();
Assert.True(result);
}
}

基本上,当我从 TestB 类运行我的测试时,它达到了"_sut。OtherMethodOfA()',在该方法中需要调用"MethodOfA()",它应该只使用我在 Mock 中提供的值并继续执行,而不是调用实际方法(它现在正在这样做)。

现在,如果我的测试方法更简单并且不在其中调用另一个方法,那么整个事情就会很简单(我的许多其他方法都是这种情况,我已经成功地测试了这些方法),但是由于此方法在执行期间调用了另一个方法,我需要模拟这个中间方法并将其传递给我的测试方法。

这很好,因为你可以用一块石头杀死两只鸟:减少或消除继承,并使用依赖注入。

与其创建protected方法,不如将该方法执行的操作想象为类所依赖的抽象。

而不是

protected bool AnotherMethodOfA()

想象

public interface IThingThatDoesSomethingAndReturnsABoolean
{
bool MethodThatReturnsBool();
}

public delegate bool FunctionThatReturnsBool();

然后,重写类A如下所示:

public class A
{
private readonly IThingThatDoesSomethingAndReturnsABoolean _thing;
public A(IThingThatDoesSomethingAndReturnsABoolean thing)
{
_thing = thing;
}
protected bool AnotherMethodOfA()
{
bool anotherReturnValue = false;
bool operationCheck = _thing.MethodThatReturnsBool();
if (operationCheck)
{
//do something to set the value of anotherReturnValue
}
return anotherReturnValue;
}
}

如果您需要更改返回bool的任何内容的实现,则不必通过继承A来执行此操作。这是一种常见的模式,但它往往会纠结并产生问题,包括您询问的确切问题。

相反,您所要做的就是提供不同的IThingThatDoesSomethingAndReturnsABoolean实现。

现在一切都是可测试的。您可以通过提供接口的模拟来测试A。您希望返回bool的方法可测试,现在它是因为它不再是其他类的protected方法。

这称为首选组合而不是继承。您不是让类通过相互继承来协同工作,而是编写单独的类来执行单独的操作并将它们组合在一起。

如果依赖项类需要以前作为属性或字段访问的A"拥有"的值,则可以将它们设置为方法的参数。这样就可以很容易地准确地看到该方法需要从A获得多少信息。

在某些情况下,继承是有意义的,但是将功能分离到依赖项中而不是将它们构建到继承层次结构中是一种很好的做法,这将使您的代码可测试并防止以后可能出现的其他麻烦。

最新更新