微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Azure 函数单元测试模拟 HttpClientFactory

如何解决Azure 函数单元测试模拟 HttpClientFactory

我正在尝试为 azure 函数创建单元测试并获得不受支持的表达式: 不可覆盖的成员不得用于设置/验证表达式。

这是我的函数

public class EmployeeFunction
    {
        private readonly EmpRepo _repo;

        public EmployeeFunction(EmpRepo repo) 
        {
            _repo = repo;
        }

        [FunctionName("EmployeeFunctionTrigger")]
        public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function,"get",Route = "EmployeeFunction/{empName}")] HttpRequest req,string empName,ILogger log)
        {
            //code
            return _repo.GetEmployeeByName(empName);
        }
     }

这是我在其中注入 IHttpClientFactory 的存储库

    public class EmpRepo : IEmpRepo 
    {
        private readonly IHttpClientFactory _httpClientFactory;
        public EmpRepo (IHttpClientFactory clientFactory)
        {
            _httpClientFactory = clientFactory;
        }

         public async Task<Employee> GetEmployeeByName(string name)
         {
            using HttpClient _httpClient = httpClientFactory.CreateClient("EmpClient"){
             //code
          }
     }

这是我的测试。我正在使用 Xunit 和 Moq

public class EmpFunctionTest
{
    [Fact]
        public async Task Http_trigger_should_return_kNown_string()
        {
            // Arrange  
            var httpClientFactory = new Mock<IHttpClientFactory>();
            var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
            var fixture = new Fixture();

            mockHttpMessageHandler.Protected()
                .Setup<Task<HttpResponseMessage>>("SendAsync",ItExpr.IsAny<HttpRequestMessage>(),ItExpr.IsAny<CancellationToken>())
                .ReturnsAsync(new HttpResponseMessage
                {
                    StatusCode = HttpStatusCode.OK,Content = new StringContent(fixture.Create<String>()),});

            var client = new HttpClient(mockHttpMessageHandler.Object);
            client.BaseAddress = fixture.Create<Uri>();
            httpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(client);
       
         

            var empRepo= new Mock<EmpRepo>(httpClientFactory.Object);
            empRepo.Setup(o => o.GetEmployeeByName(It.IsAny<string>()))
              // prepare the expected response of the mocked http call
              .ReturnsAsync(new Employee()
              {
                  Name= "Test",ID= 12345,})
              .Verifiable();

              var EmpFuc= new EmployeeFunction(empRepo.Object);
            //code for act and assert
}

但得到异常

System.NotSupportedException : Unsupported expression: o => o.GetEmployeeByName(It.IsAny<string>())   Non-overridable members (here: EmpRepo.GetEmployeeByName) may not be used in setup / verification expressions.

解决方法

这是一个 XY problem

将接口而不是具体的类注入到函数中。

public class EmployeeFunction {
    private readonly IEmpRepo repo;

    public EmployeeFunction(IEmpRepo repo) {
        this.repo = repo;
    }

    [FunctionName("EmployeeFunctionTrigger")]
    public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function,"get",Route = "EmployeeFunction/{empName}")] HttpRequest req,string empName,ILogger log) {
        //code
        var x = await repo.GetEmployeeByName(empName);

        //...
    }
}

repo 应该是类型化的客户端,而不是注入 IHttpClientFactory

public class EmpRepo : IEmpRepo {
    private readonly HttpClient client;

    public EmpRepo (HttpClient client) {
        this.client = client;
        //this.client.BaseAddress = new Uri("api url"); //(optional)
    }

     public async Task<Employee> GetEmployeeByName(string name) {
         //..use client here
    }
 }

并进行相应配置

builder.Services.AddHttpClient<IEmpRepo,EmpRepo>(c => {
    // c.BaseAddress = new Uri("api url"); // as needed
});

最后,测试应该使用 async Task 而不是 async void

public class EmpFunctionTest {
    [Fact]
    public async Task Http_trigger_should_return_known_string() {
        // Arrange  
        var fixture = new Fixture().Customize(new AutoMoqCustomization());

        Mock<IEmpRepo> empRepo = fixture.Freeze<Mock<IEmpRepo>>();
        empRepo
            .Setup(o => o.GetEmployeeByName(It.IsAny<string>()))
            .ReturnsAsync(new Employee() {
                Name = "Test",ID = 12345,})
            .Verifiable();

        EmployeeFunction subject = fixture.Create<EmployeeFunction>();

        //...code for act and assert
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?