是否有可能使用Moq在C#中模拟模拟的“类型名称”?

问题描述:

我正在开发具有模块化行为的C#(基于.NET Core)的chatbot。我想开发的行为之一是一个“管理”模块(其他功能)应允许管理员通过名称动态启用或禁用其他行为。是否有可能使用Moq在C#中模拟模拟的“类型名称”?

我希望管理模块通过检查它的类型信息,并做这样的事情来确定行为的名称:

var name = behaviour.GetType().GetTypeInfo().Name.Replace("Behaviour", string.Empty).ToLowerInvariant(); 

在BDD规范我写第一,我试图建立一个“行为链”由管理模块(被测系统)和一个模拟行为组成。测试涉及发送应该导致管理模块启用或禁用模拟行为的命令。

这是我迄今所做的:

public BehaviourIsEnabled() : base("Admin requests that a behaviour is enabled") 
{ 
    var mockTypeInfo = new Mock<TypeInfo>(); 
    mockTypeInfo.SetupGet(it => it.Name).Returns("MockBehaviour"); 

    var mockType = new Mock<Type>(); 
    mockType.Setup(it => it.GetTypeInfo()).Returns(mockTypeInfo.Object); 

    // TODO: make mock behaviour respond to "foo" 
    var mockBehaviour = new Mock<IMofichanBehaviour>(); 
    mockBehaviour.Setup(b => b.GetType()).Returns(mockType.Object); 

    this.Given(s => s.Given_Mofichan_is_configured_with_behaviour("administration"), AddBehaviourTemplate) 
     .Given(s => s.Given_Mofichan_is_configured_with_behaviour(mockBehaviour.Object), 
       "Given Mofichan is configured with a mock behaviour") 
      .And(s => s.Given_Mofichan_is_running()) 
     .When(s => s.When_I_request_that_a_behaviour_is_enabled("mock")) 
      .And(s => s.When_Mofichan_receives_a_message(this.JohnSmithUser, "foo")) 
     .Then(s => s.Then_the_mock_behaviour_should_have_been_triggered()) 
     .TearDownWith(s => s.TearDown()); 
} 

的问题,当我跑这是GetTypeInfo()Type扩展方法,所以起订量抛出该异常:

表达引用了一个不属于嘲讽对象的方法:it => it.GetTypeInfo()

另一种方法是,我可以将Name属性添加到IMofichanBehaviour,但我不喜欢为生产代码添加任意方法/属性的想法,这只是为了测试代码的好处。

+0

显示的扩展方法。扩展方法(静态)会使事情难以测试,具体取决于方法的复杂性以及单纯为了可测试性而应该尝试避免静态类和方法的事实。 – Nkosi

+0

@Nkosi你是什么意思显示扩展方法?我在文章中给出了它:['GetTypeInfo()'](https://msdn.microsoft.com/en-us/library/system.reflection.introspectionextensions.gettypeinfo(v = vs.110).aspx) 。你是对的,扩展/静态方法是最好的避免,但在这种情况下,我没有太多的选择,因为它是一个内置的,我必须用来检查.NET核心中的类型信息。 – Tagc

+1

然后使用一个假的:即公共类MockBehaviour:IMofichanBehaviour { – Nkosi

保持简单,假的类可以满足它被嘲笑的地方。然后

public class MockBehaviour : IMofichanBehaviour { ... } 

和测试会是什么样子

public BehaviourIsEnabled() : base("Admin requests that a behaviour is enabled") { 

    // TODO: make mock behaviour respond to "foo" 
    var mockBehaviour = new MockBehaviour(); 


    this.Given(s => s.Given_Mofichan_is_configured_with_behaviour("administration"), AddBehaviourTemplate) 
     .Given(s => s.Given_Mofichan_is_configured_with_behaviour(mockBehaviour), 
       "Given Mofichan is configured with a mock behaviour") 
      .And(s => s.Given_Mofichan_is_running()) 
     .When(s => s.When_I_request_that_a_behaviour_is_enabled("mock")) 
      .And(s => s.When_Mofichan_receives_a_message(this.JohnSmithUser, "foo")) 
     .Then(s => s.Then_the_mock_behaviour_should_have_been_triggered()) 
     .TearDownWith(s => s.TearDown()); 
} 
+0

+1,这很简单但有效。我面临着自己没有想到的问题。我之前正在研究“Name/Id”的想法,并且实际上已经沿着这条路线走下去了,但如果我再次需要这种行为,这是一个很好的方法。除非有人发表了一种方式来模拟Moq中的类型信息,这也会被接受,这会稍微方便些,但绝非必要。 – Tagc