Настройка тестового сервера OData

Пытаясь настроить модульные/интеграционные тесты для некоторых расширений, я пишу для класса OdataQueryOptions. Я использую .net core 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.

Вместо этого я создаю TestServer с помощью IWebHostBuilder:

          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;
    }

Затем создайте TestServer:

          [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 имеет значение null?

1 ответ

Никогда не было решения для использования TestServer, но я нашел обходной путь. В конце концов мне понадобился класс OdataQueryOptions, сгенерированный фреймворком. Поэтому я создал IClassFixture<> в Xunit, чтобы создать его вручную.

      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();
    }

Использование фикстуры класса в тестовом классе для создания 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 был бы проще, но он выполняет свою работу.

Другие вопросы по тегам