如何模拟抽象基类

11
我有一个名为"Question"的基类,以及几个子类例如"TrueFalse", "MultipleChoice", "MatchPairs"等等。
基类有一些方法包含逻辑,所有的子类都会用到,例如发送分数和引发事件。
我已经针对子类创建了单元测试,但是我不确定如何为基类的方法设置单元测试。
我进行了一些搜索,并且了解到我需要创建一个该类的 Mock 对象,但是我不知道如何做,因为我之前只看到过如何对可实例化对象进行操作。
在项目中,我已经安装了Moq和NUnit,所以最好能够使用它们。这是我第一次添加单元测试,因为我还很新手,所以非常感谢您给予的任何建议。
我首先在网站上进行了搜索,并找到了一些类似的问题,但是它们没有提供任何关于如何完成这个过程的示例,只是说需要将其模拟化。
非常感谢。

有一个测试虚拟类来扩展它……? - cHao
所以你基本上需要知道如何模拟一个抽象类。就是这样-在谷歌上搜索,你应该能找到你需要的(我想帮你,但我从未使用过moq)。 - Adam Rackis
@cHao 是的,这很有道理。所以我只需创建一个虚拟子类并将所有内容直接传递给基类即可。当你想到这一点时,它就很明显了。其他文章让我感到困惑,因为它们似乎推荐使用Moq框架来实例化抽象基类的实例,而我无法找到如何做到这一点。我将在我的测试项目中创建一个虚拟类,完全不使用Moq。 - Guerrilla
@AdamRackis 是的,我已经谷歌搜索过了,并找到了人们谈论它的内容,但没有代码示例。 - Guerrilla
使用moq创建您的抽象基类的模拟。就是这样。这就是模拟框架的用途。 - Adam Rackis
3个回答

14

根据这个答案,看起来你需要的是以下类似的内容:

[Test]
public void MoqTest()
{
    var mock = new Moq.Mock<AbstractBaseClass>();            
    // set the behavior of mocked methods
    mock.Setup(abs => abs.Foo()).Returns(5);

    // getting an instance of the class
    var abstractBaseClass = mock.Object;
    // Asseting it actually works :)
    Assert.AreEqual(5, abstractBaseClass.Foo());
}

谢谢你,亚当。这就是我所需要的,我不知道为什么我在搜索时找不到它,谢谢。 - Guerrilla
1
我无法让它正常工作。我收到错误信息“方法不是公共的”。我使用了var mock = new Mock<Question>(new object[]{"question text",true}); var Question = mock.Object; Question.Ask(); mock.Verify(m => m.Ask());。该方法是公共的,但我认为问题在于该类是抽象的。 - Guerrilla
@guerr - 我认为你设置模拟的方式不对。我会按照代码的方式创建它,然后使用Setup或其他moq提供的方法设置模拟值。 - Adam Rackis
如果我使用setup来定义方法的返回值,那么我是在测试方法内部的逻辑吗?不是用于当我测试一个使用另一个类的类时,我想要仿造它所返回的行为吗?抱歉问了这些初学者的问题,我已经阅读了很多,但这是我第一次实际解决这个问题。非常感谢您的帮助。 - Guerrilla
我创建了一个新的问题,包含我使用的代码和收到的消息,以便更清晰 - https://dev59.com/kmIj5IYBdhLWcg3wIx7W - Guerrilla
显示剩余2条评论

0

这个问题没有简单的测试方法,

最好的选择是:

  • 将基类方法标记为虚拟的
  • 为每个“TrueFalse”、“MultipleChoice”、“MatchPairs”类创建测试类,并覆盖虚拟方法并调用公共抽象方法。

例如,您有以下继承结构

class Question {
     protected virtual bool CheckCorrect(params int[] answers){
          return answers.Any(x => x== 42);
     }
}

class TrueFalse: Question {
     
     public int SelectedAnswer {get;set;}
     public bool IsCorrect(){
          return CheckCorrect(SelectedAnswer );
     }
}
class MultipleChoice: Question {
     public int[] SelectedAnswers {get;set;}
     public bool IsCorrect(){
          return CheckCorrect(SelectedAnswers );
     }
}

以下是这个测试方法:

abstract class TrueFalseTest: TrueFalseTest{

     public abstract bool CheckCorrectReal(params int[] answers);
     
     public override bool CheckCorrect(params int[] answers){
          return CheckCorrect(SelectedAnswer );
     }
}

abstract  class MultipleChoiceTest: MultipleChoice {
     public abstract bool CheckCorrectReal(params int[] answers);
     
     public override bool CheckCorrect(params int[] answers){
          return CheckCorrect(SelectedAnswer );
     }
}

还有测试方法本身:

class TestQuestionForms{
    [Fact]
    public void TrueFalseTest_ShouldExecute_CheckCorrectReal()
    {
        //setup
        var mock = new Mock<TrueFalseTest>();

        mock.Setup(q => q.CheckCorrectReal(It.IsAny<int[] answers>))
            .Returns(true);

        //action
        mock.Object.IsCorrect();
       
       //assert
       mock.Verify(db => db.CheckCorrectReal());

    }
    [Fact]
    public void MultipleChoiceTest_ShouldExecute_CheckCorrectReal()
    {   
        //setup
        var mock = new Mock<MultipleChoiceTest>();

        mock.Setup(q => q.CheckCorrectReal(It.IsAny<int[] answers>))
            .Returns(true);

        //action
        mock.Object.IsCorrect();
       
       //assert
       mock.Verify(db => db.CheckCorrectReal());

    }
}

在 MultipleChoiceTest 抽象类中覆盖 CheckCorrect 的目的是什么,当它在你的测试中并没有被执行。我有什么遗漏吗? - devyJava

0
我试图模拟一个抽象类,但这种方式对我没起作用。成功的方法是创建一个继承该抽象类的新类。
class newclass : abstractClass
{
}

这样我就可以设置属性并测试主方法


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接