Как заставить мой тестовый сервер С# специально генерировать исключение?

У меня есть класс ErrorHandlingMiddleware, который используется в нескольких проектах и ​​поэтому находится в общей библиотеке (а не в API), и я хочу написать для него несколько тестов. Я написал проходной тест с использованием TestServer (как описано в этой статье), который доказывает, что мое промежуточное ПО работает, когда конечная точка не выдает никаких исключений, но у меня возникли проблемы с тем, чтобы понять, как намеренно генерировать исключение, чтобы вызвать часть обработки ошибок.

Единственное, что мне удалось найти по этому поводу, это этот вопрос, который буквально противоположен тому, что я хочу.

Вот мой промежуточный код - ErrorHandlingService - это то, что просто хорошо обрабатывает и форматирует все мои ошибки.

        public ErrorHandlingMiddleware(RequestDelegate next, IErrorHandlingService errHandler)
        {
            _next = next;
            _errHandler = errHandler;
        }

        public async Task Invoke(HttpContext context, IHostEnvironment env)
        {
            try
            {
                _errHandler.AddRequestInformation(Guid.NewGuid(), DateTime.Now, $"{context.Request.Method} {context.Request.Path}");

                await _next(context);
            }
            catch (Exception ex)
            {
                _errHandler.AddError(new MyError()
                {
                    Title = "An unknown error occurred during execution",
                    ErrorID = "---",
                    ErrorMessage = env.IsProduction() ? "" : ex.ToString(),
                    HttpCode = HttpStatusCode.InternalServerError
                });

                context.Response.StatusCode = (int)_errHandler.ErrorResponse.StatusCode;

                await context.Response.WriteAsJsonAsync(_errHandler.ErrorResponse);
            }
        }

И мой проходной тест, который проверяет отсутствие исключений.

    public class ErrorHandlingMiddlewareTests
    {
        private async Task<IHost> GetHost()
        {
            return await new HostBuilder()
                .ConfigureWebHost(webBuilder =>
                {
                    webBuilder
                        .UseTestServer()
                        .ConfigureServices(services =>
                        {
                            services.Add(ServiceDescriptor.Scoped<IErrorHandlingService, ErrorHandlingService>());
                        })
                        .Configure(app =>
                        {
                            app.UseMiddleware<ErrorHandlingMiddleware>();
                        });
                })
                .StartAsync();
        }

        [Fact]
        public async Task NewRequest_AddsRequestInformation()
        {
            // Arrange
            using var host = await GetHost();
            var requestRoute = "/route/to/endpoint";

            var server = host.GetTestServer();
            server.BaseAddress = new Uri("https://example.com/");

            // Act
            var context = await server.SendAsync(c => //throws a 404, I'm not testing for that
            {
                c.Request.Method = HttpMethods.Get;
                c.Request.Path = requestRoute;
            });

            // Assert
            var errorResponse = host.Services.GetService<IErrorHandlingService>().ErrorResponse;
            errorResponse.ErrorContent.Request.Should().Be("GET " + requestRoute);
            errorResponse.ErrorContent.TraceID.Should().NotBeEmpty();
        }
    }

Итак, как мне теперь заставить тестовый сервер генерировать исключение, которое может быть перехвачено моим промежуточным программным обеспечением?

Мне кажется, вы можете создать образец веб-приложения специально для тестирования и использовать тестовый сервер для этого приложения. У вас может быть конечная точка, специально предназначенная для создания исключения, или у вас может быть конечная точка общего назначения, которая вызывает службу, которую вы можете имитировать, чтобы создавать разные поведения для каждого из ваших тестов. См. scotthannen.org/blog/2021/11/18/…

StriplingWarrior 30.03.2023 18:52

Вместо этого вы можете написать модульные тесты. Просто сделайте так, чтобы макет выдал исключение.

beautifulcoder 30.03.2023 19:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
65
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Один из способов — перегрузить обработчик флагом и обернуть его в DEBUG, чтобы его не было в производственных сборках. В тестах, где вы хотите получить ответ об ошибке, отправьте true в качестве третьего параметра.

#if DEBUG
public async Task Invoke(HttpContext context, IHostEnvironment env, isErrorTest)
{
  if (isErrorTest)
  {
    // Either build and respond with your error here, or possibly set something up on the server that would cause the normal handler to throw the error you want
    _errHandler.AddError(new MyError()
    {
      // ...
    });
    // ... error response
  }

  // if not error test, call your normal handler
}
#endif

Это яркий пример «повреждения, вызванного тестами»: код промежуточного программного обеспечения и API не нужно загрязнять знаниями о тестах, которые на нем выполняются.

StriplingWarrior 30.03.2023 18:50
Ответ принят как подходящий

Итак, менее чем через 24 часа я решил, что, как обычно, мне просто нужно было поспать на нем. Комментарий StriplingWarrior указал мне на руководство, которое, хотя и не говорило ничего, чего я еще не знал, все же побудило меня задуматься о том, как на самом деле строится приложение в .NET и где обычный API будет искать контроллеры/маршруты.

Короче говоря, после некоторых проб и ошибок и некоторых исследований того, как работает Startup.cs, и некоторых основ маршрутизации вот как я решил это:

        private async Task<IHost> GetHost()
        {
            return await new HostBuilder()
                .ConfigureWebHost(webBuilder =>
                {
                    webBuilder
                        .UseTestServer()
                        .ConfigureServices(services =>
                        {
                            services.AddRouting(); // <- new line
                            services.Add(ServiceDescriptor.Scoped<IErrorHandlingService, ErrorHandlingService>());
                        })
                        .Configure(app =>
                        {
                            app.UseRouting(); // <- new line
                            app.UseMiddleware<ErrorHandlingMiddleware>();
                            app.UseEndpoints(endpoints => // <- new section
                            {
                                endpoints.MapGet("/hello", () => "Hello World!");
                                endpoints.MapGet("/err", () => { throw new Exception(); });
                            });
                        });
                })
                .StartAsync();
        }

Использование некоторых встроенных определений новых конечных точек означает, что я могу легко контролировать то, что входит и выходит, и потенциально могу сделать ответы более сложными, если мне нужно. Я предполагаю, что довольно редко вы тестируете материал в библиотеке классов с локальным TestServer, но я надеюсь, что это будет полезно для следующего человека, у которого нет класса Startup для использования.

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