单元测试来验证是否调用了基类方法

问题描述 投票:0回答:4

我有一个基类:

public abstract class MyBaseClass
{
    protected virtual void Method1()
    {    
    }
} 

和派生类:

public class MyDerivedClass : MyBaseClass
{
    public void Method2()
    {
        base.Method1();
    }
}

我想为

Method2
编写一个单元测试,以验证它是否在基类上调用
Method1
。我使用 Moq 作为我的模拟库。这可能吗?

我发现了一个相关的SO链接:

使用 Moq 模拟基类方法调用

其中第二个答案表明可以通过在模拟对象上将

CallBase
属性设置为 true 来实现。然而,尚不清楚这将如何验证对基类方法(上例中的
Method1
)的调用。

感谢对此的任何帮助。

c# unit-testing moq
4个回答
18
投票

单元测试应该验证行为,而不是实现。造成这种情况的原因有几个:

  • 结果才是目标,而不是你如何获得结果
  • 测试结果允许您改进实施,而无需重新编写测试
  • 实现更难模拟

可能能够放入钩子或创建模拟来验证基本方法是否被调用,但是您真的关心如何获得答案,还是您关心答案是否正确

如果您需要的特定实现具有可以验证的副作用,那么 that 就是您应该验证的。


6
投票

从派生类的角度模拟基类是不可能的。在您的简单示例中,我建议选择两个选项之一。

选项1:如果

MyDerivedClass
真的不应该关心
MyBaseClass
在做什么,那么使用依赖注入!耶,抽象!

public class MyClass
{
    private readonly IUsedToBeBaseClass myDependency;

    public MyClass(IUsedToBeBaseClass myDependency){
        _myDependency = myDependency;
    }

    public void Method2()
    {
        _myDependency.Method1();
    }
}

测试土地的其他地方...

[TestClass]
public class TestMyDependency {
    [TestMethod]
    public void TestThatMyDependencyIsCalled() {
        var dependency = new Mock<IUsedToBeBaseClass>();
        var unitUnderTest = new MyClass(dependency.Object);
        var unitUnderTest.Method2();
        dependency.Verify(x => x.Method1(), Times.Once);
    }
}

选项 2:如果

MyDerivedClass
需要知道
MyBaseClass
在做什么,则 测试
MyBaseClass
正在做正确的事情

在替代试验地...

[TestClass]
public class TestMyDependency {
    [TestMethod]
    public void TestThatMyDependencyIsCalled() {
        var unitUnderTest = new MyDerivedClass();
        var unitUnderTest.Method2();
        /* verify base class behavior #1 inside Method1() */
        /* verify base class behavior #2 inside Method1() */
        /* ... */
    }
}

4
投票

您所描述的不是对代码的测试,而是对语言行为的测试。这很好,因为这是确保语言按照我们认为的方式运行的好方法。我在学习时曾经编写过很多小型控制台应用程序。我希望当时我就知道单元测试,因为这是一种更好的方法。

但是一旦您测试了它并确认语言的行为符合您的预期,我就不会继续为此编写测试。您可以只测试代码的行为。

这是一个真正简单的例子:

public class TheBaseClass
{
    public readonly List<string> Output = new List<string>();

    public virtual void WriteToOutput()
    {
        Output.Add("TheBaseClass");
    }
}

public class TheDerivedClass : TheBaseClass
{
    public override void WriteToOutput()
    {
        Output.Add("TheDerivedClass");
        base.WriteToOutput();
    }
}

单元测试

    [TestMethod]
    public void EnsureDerivedClassCallsBaseClass()
    {
        var testSubject = new TheDerivedClass();
        testSubject.WriteToOutput();
        Assert.IsTrue(testSubject.Output.Contains("TheBaseClass"));
    }

0
投票

这是一个特殊且简单的情况,因为方法有不同的名称,当我们需要测试对重写方法的某些调用时就会出现问题。

请注意:

  • 我避免调用显式的base.Method1(),只调用Method1()。
  • 我已经向调用添加了参数,以便也测试参数调用。

这里是代码:

 using Moq;
 using Moq.Protected;

 public abstract class MyBaseClass
 {
     protected virtual void Method1(string param)
     {
     }
 }

 public class MyDerivedClass : MyBaseClass
 {
     public void Method2(string param)
     {
         Method1($"called=>{param}");
     }
 }


 [TestMethod]
 public void Method2_MustCallMethod1()
 {
     var mock = new Mock<MyDerivedClass>();
     var testClass = mock.Object;

     var param = "someRandomValue";
     var callParam = $"called=>{param}";

     mock.Protected()
         .Setup("Method1", ItExpr.IsAny<string>());

     testClass.Method2(param);

     mock.Protected()
         .Verify("Method1", Times.Once(), callParam);

 } 
© www.soinside.com 2019 - 2024. All rights reserved.