测试使用NUnit调用私有方法的公共方法

时间:2015-07-09 04:46:51

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

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

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进行模拟。我该怎么做?因为,它在内部调用这个我不想公开的私有方法。或者,如果我将私有方法转为受保护的方式,有没有办法呢?

3 个答案:

答案 0 :(得分:4)

  

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

这很棒。这就是你应该做的。暂时忘掉内部细节。从公共方法的角度来看,这两个片段之间有什么区别吗?

// 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)。

答案 1 :(得分:3)

是的“技巧”是使用protected而不是private,然后继承该类并对执行protected方法的新类运行测试。 这是使棕色区域和遗留代码可测试的一种非常常见的方法。

    [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);
        }

    }

答案 2 :(得分:1)

虽然目前你必须重构你的代码以松开私有修饰符(包装器和不包含的东西),你可以使用像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);

}