Search code examples
c#.netbotframework

How do you Mock LUIS during Unit Tests?


  1. I have been searching for quick samples, tutorials, etc. about how to mock LUIS using Moq or what not in unit tests on Microsoft Botframework, but found none. Some in the net are 2 years old and the link to Github are unavailable. I hope somebody can help me here. Some details to the bot:
    • Language: C# 4.4
    • Framework: .NET 4.6
    • Botframework SDK: 3.x

Example code:

            public async Task Process(IDialogContext context, LuisResult luisResult)
            {
                string emoji = luisResult.Query;
                if (emoji.Contains(":)") || emoji.Contains(": )"))
                {
                    await context.PostAsync(":)");
                }
            }

Solution

  • LuisResult is not an Interface making it harder to use mocking Frameworks in general.

    You can create your own interface and an implementation that forwards all calls so that you can then mock that interface.

    internal interface ILuisResult
    {
        string Query { get; }
    }
    
    internal class LuisResultAdapter : ILuisResult
    {
        private readonly LuisResult _luisResult;
    
        public LuisResultAdapter(LuisResult luisResult)
        {
            _luisResult = luisResult;
        }
    
        public string Query => _luisResult.Query;
    }
    

    Alternatively you can refactor the Process so that it no longer directly depends on LuisResult by wrapping just the call with a function and pass in a lambda for testing.

        public void CallOptions(IDialogContext context, LuisResult luisResult)
        {
            Process(context, () => luisResult.Query).Wait();
            Process(context, () => "testData").Wait();
        }
    
    
        public async Task Process(IDialogContext context, Func<string> query)
        {
            string emoji = query();
            if (emoji.Contains(":)") || emoji.Contains(": )"))
            {
                await context.PostAsync(":)");
            }
        }
    

    EDIT: As per request in the comments here is a more detailed example for testing.

    [TestFixture]
    public class ProcessFixture
    {
        private ILuisResult _luisResult;
        private BotHost _tested;
        private IDialogContext _dialogContext;
        private string _posted = null;
    
        [SetUp]
        public void SetUp()
        {
            _posted = null;
            _luisResult = Rhino.Mocks.MockRepository.GenerateMock<ILuisResult>();
            _dialogContext = Rhino.Mocks.MockRepository.GenerateMock<IDialogContext>();
            _dialogContext
                .Stub(x => x.PostAsync(Arg<string>.Is.Anything))
                .Do((Func<string, Task>) (s =>
                {
                    _posted = s;
                    return Task.Factory.StartNew(() => { });
                }));
            _tested = new BotHost(); //this is a made up class so I can call a method on it
        }
    
        [TestCase("", ExpectedResult = null)]
        [TestCase(":)", ExpectedResult = ":)")]
        [TestCase(": )", ExpectedResult = ":)")]
        public string ProcessCleansUpInputs(string input)
        {
            _luisResult.Stub(x => x.Query).Return(input);
            _tested.Process(_dialogContext, _luisResult).Wait();
            return _posted;
        }
    }
    

    packages in use for this:

    "NUnit" version="3.11.0" so that i have an xunit framework

    "NUnit3TestAdapter" version="3.11.2" so that I can run the tests in my vs ide using Test->Windows->Test Explorer

    "RhinoMocks" version="3.6.1" so that i can create stubs from interfaces