首页 > 解决方案 > 如何模拟从 Azure Function 调用的方法?

问题描述

Azure Functions 的问题是它们的 Run 函数必须是静态的。此 Run 函数调用一个执行数据库搜索查询的函数。我很想模拟这个功能

namespace Something.App{
    public class Something {        
        [FunctionName("Something")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
            ILogger log)
        {   
            //perform something
            dbCall(); // I wish to mock
            //perform something
            return new OkObjectResult(result); 
        }

然而,对此的嘲笑尝试失败了。测试本身运行,但它不运行模拟版本,它只是运行使用互联网连接的原始版本。

namespace Something.Test{
    public class SomethingTest{
        private readonly ILogger logger = TestFactory.CreateLogger();

        private Mock<Something> CreateMockObject(){

            var mock = new Mock<Something>();

            mock.SetupSequence(f => f.dbCall()).Returns(something);
            return mock;
        }

        [Fact]

        public async void Http_Respond_On_Valid_Data()
        {
            CreateMockObject();
            Dictionary<string,StringValues> postParam= new Dictionary<string,StringValues>();
            postParam.Add("param", "value");

            var request = new DefaultHttpRequest(new DefaultHttpContext()){
                Query = new QueryCollection(postParam)
            };

            var response = (OkObjectResult)await Something.Run(request, logger);
            string stringResponse = (String) response.Value;
            Assert.Equal("XKCD", stringResponse);
        }

我试图将该方法分成一个非静态类(比如说SomethingTool)并实现这样的目标。

namespace Something.App{
    public class Something {        
        [FunctionName("Something")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
            ILogger log)
        {   
            SomethingTool object = new SomethingTool();
            //perform something
            object.dbCall(); // I wish to mock
            //perform something
            return new OkObjectResult(result); 
        }

但它并没有完成这项工作。对于这个项目,使用 Moq 是必须的。

标签: c#moq

解决方案


最简单的方法是重构函数以抽象出依赖关系,以便在测试时可以根据需要替换它们

例如

public static class Something {

    public static Func<ISomeDependency> Factory = () => return new SomeDependency();

    [FunctionName("Something")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
        ILogger log) { 

        //...

        ISomeDependency tool = Factory.Invoke();
        var result = await tool.dbCall();

        //...

        return new OkObjectResult(result); 
    }
}

现在可以像这样测试

public class SomethingTest{
    private readonly ILogger logger = TestFactory.CreateLogger();

    [Fact]
    public async Task Http_Respond_On_Valid_Data() {
        //Arrange
        var expected = "XKCD";
        var mock = new Mock<ISomeDependency>();
        mock.Setup(_ => _.dbCall()).ReturnsAsync(expected);

        Something.Factory = () => return mock.Object; //<-- replace factory delegate

        var postParam = new Dictionary<string, StringValues>();
        postParam.Add("param", "value");

        var request = new DefaultHttpRequest(new DefaultHttpContext()){
            Query = new QueryCollection(postParam)
        };

        //Act
        var response = (OkObjectResult)await Something.Run(request, logger);
        string actual = (String) response.Value;

        //Assert
        Assert.Equal(expected, actual);
    }
}

实际实现将在调用时使用该函数,但在单独对函数进行单元测试时可以替换为工厂方法。


推荐阅读