【问题标题】:Is it possible to mock the "type name" of a mock in C# using Moq?是否可以使用 Moq 在 C# 中模拟模拟的“类型名称”?
【发布时间】:2016-11-13 12:06:03
【问题描述】:

我正在用 C#(基于 .NET Core)开发一个具有模块化行为的聊天机器人。我想要开发的行为之一是“管理”模块(在其他功能中)应该允许管理员按名称动态启用或禁用其他行为。

我希望管理模块通过检查其类型信息并执行以下操作来确定行为的名称:

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上的扩展方法,所以Moq抛出异常:

表达式引用了一个不属于被模拟的方法 对象:it => it.GetTypeInfo()

另一种方法是,我可以将Name 属性添加到IMofichanBehaviour,但我不喜欢将任意方法/属性添加到生产代码中,因为它们只是为了测试代码的利益而存在。

【问题讨论】:

  • 显示扩展方法。扩展方法(静态)使测试变得困难,这取决于方法的复杂性,以及为了可测试性而应尽量避免使用静态类和方法这一事实。
  • @Nkosi 显示扩展方法是什么意思?我已经在帖子中给出了它:GetTypeInfo()。没错,最好避免扩展/静态方法,但在这种情况下,我没有太多选择,因为它是我必须用来检查 .NET Core 中的类型信息的内置方法。
  • 然后使用假的,即:public class MockBehaviour : IMofichanBehaviour { ... }

标签: c# testing mocking moq


【解决方案1】:

用一个满足它被模拟的假类来保持简单。

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());
}

【讨论】:

  • +1,这很简单但很有效。我捂着脸说我自己没想到。我之前正在考虑“名称/ID”的想法,实际上我已经走上了这条路,但如果我再次需要这种行为,这是一个很好的方法。也可以接受,除非有人在 Moq 中发布了一种模拟类型信息的方法,这会稍微方便一些,但绝不是必需的。
猜你喜欢
  • 2021-05-28
  • 2023-04-05
  • 2018-10-22
  • 1970-01-01
  • 2013-12-23
  • 1970-01-01
  • 1970-01-01
  • 2017-02-27
  • 1970-01-01
相关资源
最近更新 更多