【发布时间】:2014-10-03 23:14:20
【问题描述】:
我想使用 moq 框架为 C# 中的私有方法编写单元测试,我在 StackOverFlow 和 Google 中进行了搜索,但找不到预期的结果。如果可以,请帮助我。
【问题讨论】:
标签: c# unit-testing moq
我想使用 moq 框架为 C# 中的私有方法编写单元测试,我在 StackOverFlow 和 Google 中进行了搜索,但找不到预期的结果。如果可以,请帮助我。
【问题讨论】:
标签: c# unit-testing moq
你不能,至少起订量不能。
但更重要的是,您不应该。 首先,你不测试方法,你测试行为。其次,为了测试行为,您可以练习一个类型的 public API 并验证该练习的结果。
私有方法是实现细节。您不想验证 如何 完成事情,您想验证事情 所做 是否完成。
【讨论】:
在你项目的 AssemblyInfo.cs 中添加
[assembly: InternalsVisibleTo("Namespace.OfYourUnitTest.Project")]
然后你将方法设为内部而不是私有。
它的好处是避免公开。
然而,正如 dcastro 所指出的,有些人强烈反对这种测试方式。
【讨论】:
也许您不应该(请参阅其他答案了解原因),但您可以使用Microsoft's Visual Studio Test Tools 执行此操作。下面给出一个简化的例子。
给定您要测试的以下类:
public class ClassToTest
{
private int Duplicate(int n)
{
return n*2;
}
}
您可以使用以下代码来测试私有Duplicate方法:
using Microsoft.VisualStudio.TestTools.UnitTesting;
// ...
[TestMethod]
public void MyTestMethod()
{
// Arrange
var testClass = new ClassToTest();
var privateObject = new PrivateObject(testClass);
// Act
var output = (int) privateObject.Invoke("Duplicate", 21);
// Assert
Assert.AreEqual(42, output);
}
【讨论】:
简单地说,你不知道。私有方法对其他类不可见。
有很多方法可以解决这个问题:
对于公共方法(选项三),可以部分模拟可以替换方法的类。在起订量中,您可以这样做:
var moq = new Mock<MyClass>();
moq.CallBase = true;
moq.Setup(x => x.MyPublicMethodToOverride()).Returns(true);
还有更多详情here。
【讨论】:
Moq 支持模拟 protected 方法。 将方法更改为protected,而不是private,将允许您模拟它们的实现。
以下来自Moq Quickstart Documentation(deep link):
为受保护成员设置期望(您无法获得 IntelliSense 对于这些,因此您可以使用成员名称作为字符串访问它们)。 假设以下具有受保护函数的类应该是 嘲讽:
public class CommandBase {
protected virtual int Execute(); // (1)
protected virtual bool Execute(string arg); // (2)
}
// at the top of the test fixture
using Moq.Protected;
// In the test, mocking the `int Execute()` method (1)
var mock = new Mock<CommandBase>();
mock.Protected()
.Setup<int>("Execute")
.Returns(5);
// If you need argument matching, you MUST use ItExpr rather than It
// planning on improving this for vNext (see below for an alternative in Moq 4.8)
// Mocking the `bool Execute(string arg)` method (2)
mock.Protected()
.Setup<bool>("Execute",
ItExpr.IsAny<string>())
.Returns(true);
Moq 4.8 及更高版本允许您通过 完全不相关的类型,具有相同的成员,因此提供 IntelliSense 工作所需的类型信息。捡起 从上面的项目符号点的例子,你也可以使用这个接口 设置受保护的泛型方法和具有 by-ref 的方法 参数:
// Completely unrelated Interface (CommandBase is not derived from it) only created for the test.
// It contains a method with an identical method signature to the protected method in the actual class which should be mocked
interface CommandBaseProtectedMembers
{
bool Execute(string arg);
}
mock.Protected().As<CommandBaseProtectedMembers>()
.Setup(m => m.Execute(It.IsAny<string>())) // will set up CommandBase.Execute
.Returns(true);
【讨论】: