配置 OData 测试服务器

Configure OData Test Server

尝试为我为 OdataQueryOptions 编写的一些扩展设置单元/集成测试 class。我正在使用 .net 核心 3.1。

为了创建 SUT 实例 - 我需要一个 HttpRequest。我使用 WebApplicationFactory

创建的
public class TestingWebApplicationFactoryFixture : WebApplicationFactory<TestStartUp>
{
    protected override IHostBuilder CreateHostBuilder()
    {
        var builder = Host.CreateDefaultBuilder();
        builder.ConfigureWebHost(hostBuilder =>
        {
            hostBuilder.ConfigureServices(services =>
            {
                services.AddMvc(options => options.EnableEndpointRouting = false);
                services.AddOData();
            }).Configure(app =>
            {
                app.UseMvc(routeBuilder =>
                {
                    routeBuilder.EnableDependencyInjection();
                    routeBuilder.Select().Expand().OrderBy().Filter().MaxTop(int.MaxValue);
                });
            });
        });
        return builder;
    }

我安排测试使用 TestServer 来生成 HttpContext。然后使用 OdataQueryContext 和 HttpRequest 实例化 OdataQueryOptions 对象。

        const string path = "/?$filter=SalesOrderID eq 43659";
        var httpContext = await _testingWebApplicationFactoryFixture.Server.SendAsync(context => 
        {
            context.Request.Method = HttpMethods.Get;
            context.Request.Path = path;
        });
        var modelBuilder = new ODataConventionModelBuilder();
        modelBuilder.AddEntityType(typeof(Customer));
        var model = modelBuilder.GetEdmModel();
        var odataQueryContext = new ODataQueryContext(model, typeof(Customer), new ODataPath());
        var sut = new ODataQueryOptions<Customer>(odataQueryContext, httpContext.Request);

我在实例化对象期间遇到异常:

System.ArgumentNullException
Value cannot be null. (Parameter 'provider')
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T] 
(IServiceProvider provider)
at Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.CreateRequestScope(HttpRequest request, 
String routeName)
at Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.CreateRequestContainer(HttpRequest 
request, String routeName)
at Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.GetRequestContainer(HttpRequest request)
at Microsoft.AspNet.OData.Query.ODataQueryOptions..ctor(ODataQueryContext context, HttpRequest  
request)
at Microsoft.AspNet.OData.Query.ODataQueryOptions`1..ctor(ODataQueryContext context, HttpRequest 
request)

深入研究抛出的实际方法 - 这是因为 IServiceProvider 为 null。这不是应该由主机来处理吗?

更新:

我稍微修改了测试方法,以便删除 WebApplicationFactory class。

相反,我使用 IWebHostBuilder 创建了一个 TestServer:

    private IWebHostBuilder GetBuilder()
    {
        var webHostBuilder = new WebHostBuilder();
            webHostBuilder
                .UseTestServer()
                .ConfigureServices(services =>
                {
                    services.AddMvc(options => options.EnableEndpointRouting = false);
                    services.AddOData();
                }).Configure(app =>
                {
                    app.UseMvc(routeBuilder =>
                    {
                        routeBuilder.EnableDependencyInjection();
                        routeBuilder.Select().Expand().OrderBy().Filter().MaxTop(int.MaxValue);
                    });
                });
            return webHostBuilder;
    }

然后创建测试服务器:

    [Fact]
    public async Task QueryGenerator_Generate_SomeExpress_ShouldProduce()
    {
        const string path = "/?$filter=SalesOrderID eq 43659";
        var testServer = new TestServer(GetBuilder());
        var httpContext = await testServer.SendAsync(context =>
        {
            context.Request.Method = HttpMethods.Get;
            context.Request.Path = path;
        });
        var modelBuilder = new ODataConventionModelBuilder();
        modelBuilder.AddEntityType(typeof(Customer));
        var model = modelBuilder.GetEdmModel();
        var odataQueryContext = new ODataQueryContext(model, typeof(Customer), new ODataPath());
        var sut = new ODataQueryOptions<Customer>(odataQueryContext, httpContext.Request);
    }

我遇到了同样的异常。为什么 IServiceProvider 为空?

从未找到使用 TestServer 的解决方案,但我找到了解决方法。最后,我需要框架生成的 OdataQueryOptions class。所以我在 Xunit 中创建了一个 IClassFixture<> 来手动创建它。

public class OdataQueryOptionFixture
{
    public IServiceProvider Provider { get; private set; }
    private IEdmModel _edmModel;

    public OdataQueryOptionFixture()
    {
        SetupFixture();
    }


    public ODataQueryOptions<T> CreateODataQueryOptions<T>(HttpRequest request)
        where T : class
    {
        var odataQueryContext = CreateOdataQueryContext<T>();
        var odataQueryOptions = new ODataQueryOptions<T>(odataQueryContext, request);
        return odataQueryOptions;
    }

    private ODataQueryContext CreateOdataQueryContext<T>()
        where T : class
    {
        var odataQueryContext = new ODataQueryContext(_edmModel, typeof(T), new ODataPath());
        return odataQueryContext;
    }

    private void SetupFixture()
    {
        var collection = new ServiceCollection();
        collection.AddOData();
        collection.AddTransient<ODataUriResolver>();
        collection.AddTransient<ODataQueryValidator>();
        Provider = collection.BuildServiceProvider();
        ConfigureRoutes();
        BuildModel();
    }

    private void ConfigureRoutes()
    {
        var routeBuilder = new RouteBuilder(Mock.Of<IApplicationBuilder>(x => x.ApplicationServices == Provider));
        routeBuilder.Select().Expand().OrderBy().Filter().MaxTop(int.MaxValue).Count();
        routeBuilder.EnableDependencyInjection();
    }
    private void BuildModel()
    {
        var edmContext = new AdventureWorksEdmContext();
        _edmModel = edmContext.BuildModel();
    }

在测试中使用 class 夹具 class 构造 OdataQueryOptions

        private QueryOptionsBuilder<Customer> GetSut(HttpRequest request)
    {
        var odataQueryOptions = _odataQueryOptionFixture.CreateODataQueryOptions<Customer>(request);
        var odataQuerySettings = new ODataQuerySettings();
        var odataValidationSettings = new ODataValidationSettings();
        var customerExpandBinder = new CustomerExpandBinder(odataValidationSettings, odataQueryOptions.SelectExpand);
        var customerOrderByBinder = new CustomerOrderByBinder(odataValidationSettings, odataQueryOptions.OrderBy);
        var customerSelectBinder = new CustomerSelectBinder(odataValidationSettings, odataQueryOptions.SelectExpand);
        var customerCompositeBinder = new CustomerCompositeBinder(customerExpandBinder, customerOrderByBinder, customerSelectBinder);
        return new QueryOptionsBuilder<Customer>(customerCompositeBinder, odataQuerySettings);
    }

TestServer 会更容易 - 但这样就完成了工作。