1
votes

I'm developing a chatbot in C# (based on .NET Core) that has modular behaviours. One of the behaviours I want to develop is an "admin" module that (among other functions) should allow admins to dynamically enable or disable other behaviours by name.

I want the admin module to determine the name of a behaviour by inspecting its type info and doing something like:

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

In a BDD specification I'm writing first, I'm trying to set up a "behaviour chain" consisting of the admin module (system under test) and a mock behaviour. The tests involve sending commands that should cause the admin module to enable or disable the mock behaviour.

This is what I've done so far:

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

The problem when I run this is that GetTypeInfo() is an extension method on Type, so Moq throws the exception:

Expression references a method that does not belong to the mocked object: it => it.GetTypeInfo()

An an alternative, I could just add a Name property to IMofichanBehaviour, but I don't like the idea of adding arbitrary methods/properties to production code that's only really there for the benefit of test code.

1
Show the extension method. extension methods (static) make things difficult to test depending on the complexity of the method and also the mere fact that for testability one should try to avoid static classes and methods.Nkosi
@Nkosi What do you mean by show the extension method? I've given it in the post: GetTypeInfo(). You're right that extension/static methods are best avoided but in this case I don't have much choice as it's a built-in one I have to use to inspect type information in .NET Core.Tagc
Then use a fake i.e: public class MockBehaviour : IMofichanBehaviour { ... }Nkosi

1 Answers

1
votes

Keep it simple with a fake class that satisfies what it being mocked.

public class MockBehaviour : IMofichanBehaviour { ... }

And test would then look like

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