You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

引入Microsoft.Identity.Web后Azure Function单元测试触发TypeLoadException

Azure Functions单元测试引入Microsoft.Identity.Web后类型加载失败问题

我在对一个读取查询字符串参数的Azure Function做单元测试,引入Microsoft.Identity.Web包后,原本正常的测试抛出TypeLoadException,无法加载内部的QueryCollection或DefaultHttpRequest类型,以下是详细信息:

目标Function代码

[FunctionName("GetEntityId")]
public async Task<IActionResult> GetEntityId(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "entityid")] HttpRequest req)
{

    if (!int.TryParse(req.Query["quantity"], out int quantity))
    {
        quantity = 1;
    }

    quantity = Math.Min(quantity, 100);

    var entityIds = await this.entityIdGenerator.NewEntityIdAsync(userId, quantity);

    if (entityIds is null)
    {
        return new System.Web.Http.InternalServerErrorResult();
    }
    else
    {
        return new OkObjectResult(entityIds);
    }
}

原单元测试实现(引入包前正常)

之前通过Moq模拟HttpRequest和HttpContext,代码如下:

var reqMock = new Mock<HttpRequest>();
var httpContextMock = new Mock<HttpContext>();
var responseMock = new Mock<HttpResponse>();

responseMock.Setup(res => res.Headers).Returns(new HeaderDictionary());
httpContextMock.Setup(http => http.Response).Returns(responseMock.Object);
httpContextMock.Setup(http => http.Request).Returns(reqMock.Object);

通过内部类QueryCollection模拟查询字符串:

reqMock.Setup(req => req.Query).Returns(new QueryCollection(queryString));

报错信息

引入Microsoft.Identity.Web后,单元测试触发以下错误:

System.TypeLoadException: 'Could not load type 'Microsoft.AspNetCore.Http.Internal.QueryCollection' from assembly 'Microsoft.AspNetCore.Http, Version=3.1.31.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.'

尝试改用DefaultHttpRequest和DefaultHttpContext时,出现类似错误:

Could not load type 'Microsoft.AspNetCore.Http.Internal.DefaultHttpRequest' from assembly 'Microsoft.AspNetCore.Http, Version=3.1.31.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.'

已尝试更新所有相关包到最新版本,问题依旧。

项目CSPROJ包引用

<ItemGroup>
    <PackageReference Include="AutoMapper" Version="11.0.1" />
    <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
    <PackageReference Include="Avatar.Infrastructure.EfCore" Version="1.0.0-CI-20230130-181631" />
    <PackageReference Include="Azure.Storage.Blobs" Version="12.6.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Logging.ApplicationInsights" Version="3.0.30" />
    <PackageReference Include="Microsoft.Data.SqlClient" Version="2.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.28" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.28" />
    <PackageReference Include="Microsoft.Identity.Client" Version="4.49.1" />
    <PackageReference Include="Microsoft.Identity.Web" Version="1.25.10" />
    <PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.9" />
    <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="6.26.0" />
    <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.25.1" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.13" />
    <PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
    </PackageReference>
    <AdditionalFiles Include="stylecop.json" />
  </ItemGroup>

完整单元测试代码

[TestCategory("AvatarFunctionAppTest Story 893")]
[TestMethod]
public async Task GetEntityIdNotAuthorised()
{
    HttpRequest mockedRequest = this.HttpRequestSetup();
    var result = await new AvatarFunctionApp(
        this.EasyAuthProxy, 
        this.EntityIdGenerator, 
        this.TableStorage, 
        this.mockSignalService.Object, 
        this.log.Object)
            .GetEntityId(mockedRequest);

    Assert.IsTrue(result is UnauthorizedResult);
}

public HttpRequest HttpRequestSetup(IHeaderDictionary headers = null)
        {
            return this.HttpRequestSetup(new Dictionary<string, StringValues>(), headers);
        }

public HttpRequest HttpRequestSetup(Dictionary<string, StringValues> queryString, IHeaderDictionary headers = null, Uri uri = null)
{
    var reqMock = new Mock<HttpRequest>();
    var httpContextMock = new Mock<HttpContext>();
    var responseMock = new Mock<HttpResponse>();

    responseMock.Setup(res => res.Headers).Returns(new HeaderDictionary());
    httpContextMock.Setup(http => http.Response).Returns(responseMock.Object);
    httpContextMock.Setup(http => http.Request).Returns(reqMock.Object);

    reqMock.Setup(req => req.Query).Returns(new QueryCollection(queryString));
    reqMock.Setup(req => req.Body).Returns(default(Stream));
    reqMock.Setup(req => req.Headers).Returns(headers ?? new HeaderDictionary());
    reqMock.Setup(req => req.HttpContext).Returns(httpContextMock.Object);

    if (uri is Uri)
    {
        reqMock.Setup(req => req.Scheme).Returns(uri.Scheme);
        reqMock.Setup(req => req.Host).Returns(new HostString(uri.Host, uri.Port));
        reqMock.Setup(req => req.Path).Returns(uri.AbsolutePath);
    }

    this.mockHttpContextAccessor.Setup(hca => hca.HttpContext).Returns(httpContextMock.Object);

    return reqMock.Object;
}

解决方案

1. 避免使用内部类型,改用公开API构造查询参数

Microsoft.AspNetCore.Http.Internal.QueryCollection是内部类型,版本更新或依赖冲突时容易失效。改用公开API构造IQueryCollection:

// 替代new QueryCollection(queryString)的写法
var query = QueryString.Create(queryString);
var queryCollection = query.ParseNullable();

reqMock.Setup(req => req.Query).Returns(queryCollection);

或者直接模拟IQueryCollection的行为,避免实例化具体内部类:

var queryMock = new Mock<IQueryCollection>();
queryMock.Setup(q => q["quantity"]).Returns("5");
reqMock.Setup(req => req.Query).Returns(queryMock.Object);

2. 统一依赖版本,解决冲突

检查项目依赖中Microsoft.AspNetCore.Http的版本,确保所有引用该包的依赖使用同一版本。可以在CSPROJ中添加版本绑定:

<ItemGroup>
  <PackageReference Include="Microsoft.AspNetCore.Http" Version="3.1.31" />
</ItemGroup>

同时更新Microsoft.NET.Sdk.Functions到兼容版本(如3.0.31或更高),确保与Microsoft.Identity.Web的依赖版本匹配。

3. 使用DefaultHttpContext的正确方式

如果需要使用真实的HttpRequest实例,不要直接引用内部的DefaultHttpRequest,而是通过DefaultHttpContext获取:

var httpContext = new DefaultHttpContext();
httpContext.Request.Query = QueryString.Create(queryString).ParseNullable();
// 直接使用httpContext.Request作为测试的HttpRequest

确保项目中引用的Microsoft.AspNetCore.Http版本与DefaultHttpContext兼容,避免版本不匹配导致的类型加载错误。

内容的提问来源于stack exchange,提问作者Pat Long - Munkii Yebee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 15:20:23