使用Moq在C#中进行unit testing保护的方法

最近我注意到你可以使用Moq对抽象基类进行unit testing,而不是在测试中创建实现抽象基类的虚拟类。 请参见如何使用moq测试抽象类中的具体方法? 你可以这样做:

public abstract class MyAbstractClass { public virtual void MyMethod() { // ... } } [Test] public void MyMethodTest() { // Arrange Mock mock = new Mock() { CallBase = true }; // Act mock.Object.MyMethod(); // Assert // ... } 

现在我想知道是否有类似的技术允许我测试受保护的成员而不必创建包装类 。 即你如何测试这种方法:

 public class MyClassWithProtectedMethod { protected void MyProtectedMethod() { } } 

我知道Moq.Protected命名空间,但据我所知它只允许你设置期望与例如

 mock.Protected().Setup("MyProtectedMethod").Verifiable(); 

我也知道这里显而易见的答案是“不测试受保护的方法,只测试公共方法”,然而这是另一个争论! 我只想知道是否可以使用Moq。

更新:以下是我将如何正常测试:

 public class MyClassWithProtectedMethodTester : MyClassWithProtectedMethod { public void MyProtectedMethod() { base.MyProtectedMethod(); } } 

提前致谢。

对于初学者来说,unit testing抽象方法毫无意义。 没有实施! 您可能希望对不纯的抽象类进行unit testing,validation是否调用了抽象方法:

 [Test] public void Do_WhenCalled_CallsMyAbstractMethod() { var sutMock = new Mock() { CallBase = true }; sutMock.Object.Do(); sutMock.Verify(x => x.MyAbstractMethod()); } public abstract class MyAbstractClass { public void Do() { MyAbstractMethod(); } public abstract void MyAbstractMethod(); } 

请注意,我将CallBase设置为将其转换为部分模拟,以防Do是虚拟的。 否则Moq将取代Do方法的实现。

使用Protected()可以validation是否以类似方式调用了受保护的方法。

当您使用Moq或其他库创建模拟时,重点是覆盖实现。 测试受保护的方法涉及公开现有实现。 这不是Moq的目的。 Protected()只是让你访问(大概是通过reflection,因为它是基于字符串的)来覆盖受保护的成员。

使用调用受保护方法的方法编写测试后代类,或者在unit testing中使用reflection来调用受保护方法。

或者,更好的是,不要直接测试受保护的方法。

您已经触及了“测试公共API,而不是私有”思想过程,并且您已经提到了从类inheritance然后以这种方式测试其受保护成员的技术。 这两种都是有效的方法。

在这一切之下,简单的事实是,您认为这个实现细节(因为这是私有或受保护的成员)非常重要,可以直接测试而不是通过使用它的公共API间接测试。 如果这重要,也许重要的是推广到自己的class级。 (毕竟,如果它非常重要,也许它是MyAbstractClass不应该有的责任。)类的实例将在MyAbstractClass受到保护,因此只有基类和派生类型才能访问实例,但类本身否则将完全可测试,如果需要,可在其他地方使用。

 abstract class MyAbstractClass { protected ImportantMethodDoer doer; } class ImportantMethodDoer { public void Do() { } } 

否则,您将离开*已经确定的方法。


* Moq可能会或可能不会提供一些获取私人或受保护成员的机制,我不能说,因为我不使用该特定工具。 我的答案更多来自建筑的立场。

Moq调用受保护成员的另一种方法是以下模板:

  1. 在您的class级中,受保护的成员将您的function标记为虚拟。 例如:

     public class ClassProtected { public string CallingFunction(Customer customer) { var firstName = ProtectedFunction(customer.FirstName); var lastName = ProtectedFunction(customer.LastName); return string.Format("{0}, {1}", lastName, firstName); } protected virtual string ProtectedFunction(string value) { return value.Replace("SAP", string.Empty); } } 

然后在您的unit testing中添加引用

  using Moq.Protected; 

在您的unit testing中,您可以编写以下内容:

  [TestFixture] public class TestClassProttected { [Test] public void all_bad_words_should_be_scrubbed() { //Arrange var mockCustomerNameFormatter = new Mock(); mockCustomerNameFormatter.Protected() .Setup("ProtectedFunction", ItExpr.IsAny()) .Returns("here can be any value") .Verifiable(); // you should call this function in any case. Without calling next Verify will not give you any benefit at all //Act mockCustomerNameFormatter.Object.CallingFunction(new Customer()); //Assert mockCustomerNameFormatter.Verify(); } } 

记下ItExpr。 它应该用来代替它。 另一个问题在Verifiable等待着你。 我不知道为什么,但是没有调用Verifiable Verify也不会被调用。