Azure 函数单元测试模拟 HttpClientFactory
Azure Function UnitTesting Mock 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。
将接口而不是具体 class 注入函数。
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
}
}
我正在尝试为 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。
将接口而不是具体 class 注入函数。
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
}
}