使用NUnit测试调用私有方法的公共方法
本文关键字:方法 有方法 NUnit 测试 调用 使用 | 更新日期: 2023-09-27 18:01:39
我在一个类中有一个公共方法,它内部调用该类中的特定私有方法。它看起来像这样:
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来模拟。我该怎么做呢?因为,它在内部调用这个私有方法,我不想让它公开。或者如果我将私有方法改为保护,是否有办法做到这一点?
现在,我基本上想测试这个公共方法(…)
这太好了。这才是你该做的。暂时忘记内部细节。从公共方法的角度来看,这两个代码片段有什么区别吗?
// 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
}
调用(public) MyMethod
的人将无法注意到这两者之间的任何区别。最终的结果是一样的。有私有方法调用并不重要,因为就公共API而言,它是无关紧要的。你可以内联私有方法,让它永远消失,从公共消费者的角度来看,什么都不会改变。最终结果是唯一重要的事情。您测试代码使用者可观察到的最终结果。而不是一些内部胡言乱语。
重要的认识是:
正确设计的SOLID代码永远不会把你放在一个需要你做私有mock的位置。问题的根源是什么?糟糕的设计。
来源:如何模拟私有方法-解决方案
是的。遗憾的是,你的设计并不是很好。根据您是否想要更改它,您可以采用以下几种方法:
- 不要试图模仿私有细节,关注公共API(对设计问题没有帮助)
- 将私有方法提取到类中,引入依赖关系(长期解决方案,改善设计并使代码易于测试)
- 使私有方法受保护,在测试中根据其他答案的建议重写(对设计问题没有帮助,可能不会产生有价值的测试)
顺便说一句,(如果可以的话)不要使用NMock2。这是一个2009年更新过的图书馆。这就像一辆30年的旧车15年前才保养过一样。现在有很多更好的(FakeItEasy, Moq, NSubstitute)。
是的,"诀窍"是使用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);
}
}
虽然目前您必须重构代码以丢失私有修饰符(包装器等),但使用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);
}