【问题标题】:Test a public method which calls a private method using NUnit测试使用 NUnit 调用私有方法的公共方法
【发布时间】:2015-07-09 04:46:51
【问题描述】:

我在一个类中有一个公共方法,它在内部调用该类中的特定私有方法。它看起来像这样:

public class MyClass : IMyClassInterface
{
    public List<int> MyMethod(int a, int b)
    {
        MyPrivateMethod(a, b, ref varList, ref someVal);
    }
    private void MyPrivateMethod(int a, int b, ref List<int> varList, ref double someval)
    {
    }
}

现在,我基本上想用 NUnit 测试这个公共方法。我正在使用 NMock 2.0 进行模拟。我该怎么做?因为,它在内部调用了这个我不想公开的私有方法。或者如果我将私有方法改为受保护,有没有办法做到这一点?

【问题讨论】:

  • 私有方法是公共方法的一个实现细节。您应该将其作为测试公共方法的一部分进行测试。如果该方法没有做任何事情,您还会认为您对公共方法的测试是有效的吗?如果是这样,你为什么叫它?
  • 我不确定我们是否应该将私有方法作为公共方法的一部分进行测试。将其作为公共方法的一部分进行测试是有意义的。
  • 不过,如果我只是想检查是否调用了该私有方法,那么将其设为受保护肯定会有所帮助。
  • 这可能在短期内有所帮助,但代价是您将测试与实现紧密结合,这使得将来重构代码变得更加困难。如果感觉这可能是正确的做法,那么可能是您在类中发现了一个依赖项,可以将其提取出来单独测试并注入到您的类中。

标签: c# unit-testing nunit private-members nmock


【解决方案1】:

现在,我基本上想测试这个公共方法(...)

这很棒。这是你应该做的。暂时忘记内部细节。从public方法来看,这两个sn-ps有什么区别吗?

// Your current implementation
public void MyMethod(int a, int b)
{
    MyPrivateMethod(a, b);
}
private void MyPrivateMethod(int a, int b)
{
    var c = a + b;
    // some more code
}

// Private method inlined
public void MyMethod(int a, int b)
{
    var c = a + b;
    // some more code
}

致电(公开)MyMethod 的人将无法注意到这两者之间的任何区别。最终结果是一样的。是否有私有方法调用并不重要,因为就公共 API 而言,它是无关紧要的。你可以内联所说的私有方法,让它永远消失,从公共消费者的角度来看,没有任何改变。最终结果是唯一重要的事情。您测试代码使用者可观察到的最终结果。不是一些内部胡言乱语。

重要的实现是这样的:

正确设计的 SOLID 代码永远不会使您处于需要您进行私人模拟的位置。问题的根源?糟糕的设计。

来源:How to mock private method - solutions

是的。可悲但真实,你的设计不是那么好。根据您是否要更改它,您可以采取以下几种方法:

  • 不要试图模拟私人细节,专注于公共 API(对设计问题没有帮助)
  • 将私有方法提取到类,引入依赖项(长期解决方案,改进设计并使代码易于测试)
  • 使私有方法受到保护,按照其他答案中的建议在测试中覆盖(对设计问题没有帮助,可能不会产生有价值的测试)

无论您选择哪个,我都由您决定。但是,我将再强调一次 - 模拟私有方法 不是单元测试、库或工具问题 - 这是一个设计问题,因此最好解决。


附带说明,(如果可以的话)不要使用 NMock2。这是一个自 2009 年以来最后一次更改的图书馆。就像拥有一辆 30 年的旧车,最后一次维修是在 15 年前。现在有更好的(FakeItEasy、Moq、NSubstitute)。

【讨论】:

    【解决方案2】:

    是的,“诀窍”是使用受保护而不是私有,然后继承该类并在执行受保护方法的新类上运行测试。 这是使棕地代码和遗留代码可测试的一种非常常见的方法。

        [TestClass]
        public class UnitTest1
        {
            [TestMethod]
            public void TestMethod1()
            {
                MyClassTestWrapped t = new MyClassTestWrapped();
                Assert.IsTrue(t.MyPrivateMethod(...));
                Assert.IsTrue(t.MyMethod(...));
    
                MockFactory _factory = new MockFactory();
                Mock<MyClassTestWrapped> mock;
    
                mock = _factory.CreateMock<MyClass>();
                mock.Expects.One.MethodWith(d => d.MyPrivateMethod());  // do the nmock magic here
    
    
            }
        }
    
        public class MyClass : IMyClassInterface
        {
            public List<int> MyMethod(int a, int b)
            {
                MyPrivateMethod(a, b, ref varList, ref someVal);
            }
    // here change to protected
            protected void MyPrivateMethod(int a, int b, ref List<int> varList, ref double someval)
            {
            }
        }
    
        public interface IMyClassInterface
        {
    
        }
    
        public class MyClassTestWrapped : MyClass
        {
            public List<int> MyMethod(int a, int b)
            {
                base.MyMethod(a, b);
            }
    
            public List<int> MyPrivateMethod(int a, int b,ref List<int> varList, ref double someval)
            {
                base.MyPrivateMethod(a, b, ref varList, ref someval);
            }
    
        }
    

    【讨论】:

    • 谢谢。那应该有帮助.. 虽然你能告诉我在测试我的公共方法时如何使用 NMock 模拟受保护的方法。公共方法从其定义中调用它。或者你能引导我到一个可以进一步为我提供相同信息的网站。
    • 据我所知,您必须使用 nmock ( Mock mock; 模拟 testwrapped 类;然后对该类执行 nmock 魔术。
    • 谢谢。我试试看。
    【解决方案3】:

    虽然目前您必须重构代码以丢失私有修饰符(包装器等等),但您可以使用 Typemock Isolator 等工具轻松完成。

    我在您的示例中添加了一些代码来编写测试:

    public class MyClass 
    {
        public List<int> MyMethod(int a, int b)
        {
            List<int> varList = new List<int>();
            double someVal = 0;
    
            MyPrivateMethod(a, b, ref varList, ref someVal);
    
            return varList;
        }
    
        private void MyPrivateMethod(int a, int b, ref List<int> varList, ref double someval)
        {
        }
    }
    

    使用这种直截了当的方法,您只需伪造代码中的私有方法(生产中没有更改),即使它是 ref 参数:

    [Test]
    public void TestMethod1()
    {
        //Arrange
        var myClass = new MyClass();
        var expectedVarList = new List<int> {1,2,3};
    
        Isolate.NonPublic.WhenCalled(myClass, "MyPrivateMethod")
            .AssignRefOut(expectedVarList, 0.0)
            .IgnoreCall();
    
        //Act
        var resultVarList = myClass.MyMethod(0, 0);
    
        //Assert
        CollectionAssert.AreEqual(expectedVarList, resultVarList);
    
    }
    

    【讨论】:

      猜你喜欢
      • 2023-03-19
      • 2011-09-19
      • 1970-01-01
      • 2017-03-25
      • 1970-01-01
      • 2010-09-19
      • 1970-01-01
      • 1970-01-01
      • 2010-11-01
      相关资源
      最近更新 更多