【问题标题】:Moq parent method being called in child class在子类中调用 Moq 父方法
【发布时间】:2022-01-05 19:20:20
【问题描述】:

我有一个基类,它的受保护方法在我要测试的子类的公共方法中被调用。我没有找到一种方法来最小化基本保护方法,以便在子类中进行测试。

public class MyBaseClass
{
    protected virtual bool MyMethod(int number)
    {
        return number == 1;
    }   
}

public class MyChildClass : MyBaseClass
{
    public bool DoSomething(int number)
    {
        return MyMethod(number);
    }
}

[TestFixture]
public class MyChildClassTests
{
    [Test]
    public void Expected_Returns_False_WhenPassed_1()
    {
        var myChildClass = new MyChildClass();

        // How do I mock MyMethod used in myBaseClass here?
        // var mock = new Mock<MyBaseClass>();
        // mock.Protected().Setup<bool>("MyMethod", ItExpr.IsAny<int>()).Returns(false);
        
        // The above mock is correct, but it's in a different instance object than myBaseClass

        var result = myChildClass.DoSomething();

        Assert.AreEqual(false, result);
    }
}

我无法更改类以获得更好的架构,我必须尽我所能为DoSomething() 实现单元测试,到目前为止我所做的是模拟并准备该方法使用的所有数据,但是由于它在另一个班级,我希望我的MyChildClassTests 不做所有这些,只限于测试DoSomething()

我已经阅读了关于部分模拟和许多其他问题和答案的文章,但我无法让它正常工作。

感谢任何建议!

编辑:忘了把public 放在所有的类中,在我的真实案例中,它们是公开的。

【问题讨论】:

  • 由于您有一个虚拟方法,您可以创建一个 FakeChildClass 并覆盖 MyMethod() 。这将帮助您保护方法并对其进行单元测试。
  • 对不起,我不听。覆盖 MyMethod 时我能得到什么?我如何嘲笑它?我要单元测试的方法是 DoSomething(),我将如何使用重写的 MyMethod() 测试 FakeChildClass?
  • 你为什么要嘲笑这个?您正在对 MyChildClass 进行单元测试。这是一个非常简单的类,不需要为您计划进行的单元测试进行模拟。模拟更适合依赖于您不想启动以进行单元测试的抽象(例如,您将模拟数据层来测试业务层)。老实说,不知道在这里嘲笑你得到了什么。
  • @PaulAlanTaylor 我发布的类是简化示例,我的项目中 MyBaseClass 中的 MyMethod 非常复杂,并且调用其他 API 并具有 MyChildClass 没有的其他依赖项。这就是为什么当我为 MyChildClass 构建单元测试时,我不想提供模拟并设置 MyBaseClass 需要或使用的所有必需数据,我想模拟它并只关心 MyChildClass。 MyBaseClass 已经有自己的单元测试电池来测试 MyMethod(以及该类中的所有其他方法)的行为,我不想重复代码并再次将其放在 MyChildClass 上
  • 即使您使用简化的问题示例,如果您澄清要测试的内容可能会有所帮助,因为它并不明显。例如,在您的示例代码中的某处,您可以放置​​一行来做某事,然后澄清 - 我想确保发生这种情况。

标签: c# unit-testing moq


【解决方案1】:
class MyChildClassTests
{
    [Test]
    public void Expected_Returns_False_WhenPassed_1()
    {
        var myChildClass = new FakeChildClass();
        
        var result = myChildClass.DoSomething(1);

        Assert.AreEqual(false, result);
    }
}

public class FakeChildClass: MyChildClass
{
    protected override bool MyMethod(int number)
    {
        return number == 1;
    }
}

【讨论】:

  • 感谢 Smitha Kalluz 花时间帮助我!可悲的是,我仍然没有看到你正在做的事情的好处。 MyBaseClass 中的 MyMethod 在某些情况下返回 true,在其他情况下返回 false(如果出现问题,它甚至会抛出异常)。这不仅仅是比较一个数字,这只是一个例子。这个想法是在一个测试中模拟该方法并强制它返回 false,然后在另一个测试中模拟它以返回 true,并在另一个模拟它以引发异常。
  • 一个子类对象用于调用该方法。 Protected 用于保护成员不被类外访问。因此,通过将任何构造函数声明为受保护,我们可以从子类中调用它。我们不能调用受保护的方法。希望这会有所帮助
【解决方案2】:

首先,确保您的课程是公开的。

Moq 会抱怨无法代理到他们,如果他们不是的话。

public class MyBaseClass
{
    public virtual bool MyMethod(int number)
    {
        return number == 1;
    }   
}

public class MyChildClass : MyBaseClass
{
    public bool DoSomething(int number)
    {
        return MyMethod(number);
    }
}

接下来,公开您的基类方法。除非您这样做,否则您将无法进行设置。

之后,创建 child 对象的模拟并模拟父方法。

var mockChild = new Mock<MyChildClass>(){CallBase = true};
            mockChild.Setup(x => x.MyMethod(It.IsAny<int>())).Returns(false);

拉取你的结果.... result 将返回 false,即使实际的实现会以 1 作为参数返回 true。

var result = mockChild.Object.DoSomething(1);

当调用 DoSomething 方法时,您实际上会进入该方法的真正实现(如果您不相信我,请在上面设置一个断点!) - 但 MyMethod 的模拟版本会启动。

【讨论】:

    【解决方案3】:

    感谢大家的回复,收集所有我能够得到我用例的实际答案的信息:

    不改变MyBaseClassMyChildClass

    public class MyBaseClass
    {
        protected virtual bool MyMethod(int number)
        {
            return number == 1;
        }   
    }
    
    public class MyChildClass : MyBaseClass
    {
        public bool DoSomething(int number)
        {
            return MyMethod(number);
        }
    }
    

    我能够模拟受保护的方法并为我节省大量工作和重复代码(已经在 MyBaseClassTests 中)

    [TestFixture]
    public class MyChildClassTests
    {
        [Test]
        public void Expected_Returns_False_WhenPassed_1()
        {
            var expected = false;
            var myChildClass = new Mock<MyChildClass> {CallBase = true};
            myChildClass.Protected().Setup<bool>("MyMethod", 1).Returns(expected);
            
            var result = myChildClass.Object.DoSomething(1);
    
            Assert.AreEqual(expected, result);
        }
    
        [Test]
        public void Expected_Returns_True_WhenPassed_1()
        {
            var expected = true;
            var myChildClass = new Mock<MyChildClass> {CallBase = true};
            myChildClass.Protected().Setup<bool>("MyMethod", 1).Returns(expected);
            
            var result = myChildClass.Object.DoSomething(1);
    
            Assert.AreEqual(expected, result);
        }
    }
    

    感谢大家的帮助! :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-14
      • 2017-11-09
      • 2011-09-04
      • 2012-02-22
      • 1970-01-01
      • 2023-02-26
      • 2014-06-11
      • 1970-01-01
      相关资源
      最近更新 更多