我在类中有一个公共方法,它在内部调用该类中的特定私有方法。它看起来像这样:
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进行模拟。我该怎么做?因为,它在内部调用这个我不想公开的私有方法。或者,如果我将私有方法转为受保护的方式,有没有办法呢?
答案 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
是的。可悲但真实,你的设计并不那么好。根据您是否要更改,您可以采取的方法很少:
无论你选择哪种我都留给你。但是,我将再次强调它 - 模拟私有方法不是单元测试,库或工具问题 - 它是设计问题,并且最好可以解决。
在旁注上,(如果可以的话)不要使用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);
}