32、飞行预订服务的实现与测试

飞行预订服务实现与测试

飞行预订服务的实现与测试

1. 实现CreateBooking端点方法逻辑

在完成模型绑定后,我们可以专注于 CreateBooking 方法的核心逻辑,即调用必要的服务方法在数据库中创建预订。创建预订的一般步骤如下:
1. 验证数据绑定。
2. 确保数据库中存在提供的客户信息,如果不存在则添加。
3. 确保客户想要预订的航班存在。
4. 在 Booking 表中请求一个包含新预订的新条目。

由于我们已经实现了服务和存储库层的方法,这些步骤应该很容易实现。下面我们先从验证数据绑定开始。

为了确保 BookingData 实例处于有效状态,我们需要定义有效状态的含义。如果 FirstName LastName 属性都设置为有效的非空字符串,则该实例被认为是有效的。我们可以通过实现 IValidatableObject 接口来进行验证:

public class BookingData : IValidatableObject
{
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        List<ValidationResult> results = new List<ValidationResult>();
        if (FirstName == null && LastName == null)
        {
            results.Add(new ValidationResult("All given data points are null"));
        }
        else if (FirstName == null || LastName == null)
        {
            results.Add(new ValidationResult("One of the given data points is null"));
        }
        return results;
    }
}

在控制器方法中,我们可以检查 ModelState.IsValid 属性来确保对象有效:

[HttpPost]
public async Task<IActionResult> CreateBooking([FromBody] BookingData body)
{
    if (ModelState.IsValid)
    {
        // 继续处理
    }
    return StatusCode((int) HttpStatusCode.InternalServerError, ModelState.Root.Errors.First().ErrorMessage);
}

接下来,我们需要添加 BookingService 的支持字段和注入实例,并在 Startup 中设置依赖注入中间件:

[Route("{controller}")]
public class BookingController : Controller
{
    private BookingService _bookingService;
    public BookingController(BookingService bookingService)
    {
        _bookingService = bookingService;
    }
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddTransient(typeof(FlightService), typeof(FlightService));
    services.AddTransient(typeof(BookingService), typeof(BookingService));
    services.AddTransient(typeof(FlightRepository), typeof(FlightRepository));
    services.AddTransient(typeof(AirportRepository), typeof(AirportRepository));
    services.AddTransient(typeof(BookingRepository), typeof(BookingRepository));
    services.AddTransient(typeof(CustomerRepository), typeof(CustomerRepository));
    services.AddDbContext<FlyingDutchmanAirlinesContext>(ServiceLifetime.Transient);
    services.AddTransient(typeof(FlyingDutchmanAirlinesContext), typeof(FlyingDutchmanAirlinesContext));
}

最后,我们需要处理 flightNumber 参数,并调用 BookingService.CreateBooking 方法:

[HttpPost("{flightNumber}")]
public async Task<IActionResult> CreateBooking([FromBody] BookingData body, int flightNumber)
{
    if (ModelState.IsValid && flightNumber.IsPositiveInteger())
    {
        string name = $"{body.FirstName} {body.LastName}";
        (bool result, Exception exception) = await _bookingService.CreateBooking(name, flightNumber);
        if (result)
        {
            return StatusCode((int)HttpStatusCode.Created, exception.Message);
        }
        if (exception == null)
        {
            return StatusCode((int) HttpStatusCode.InternalServerError);
        }
        if (exception is CouldNotAddBookingToDatabaseException)
        {
            return StatusCode((int) HttpStatusCode.NotFound);
        }
        return StatusCode((int)HttpStatusCode.InternalServerError, exception.Message);
    }
    return StatusCode((int) HttpStatusCode.InternalServerError, ModelState.Root.Errors.First().ErrorMessage);
}

我们也可以使用三元运算符来简化代码:

[HttpPost("{flightNumber}")]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> CreateBooking([FromBody] BookingData body, int flightNumber)
{
    if (ModelState.IsValid && flightNumber.IsPositiveInteger())
    {
        string name = $"{body.FirstName} {body.LastName}";
        (bool result, Exception exception) = await _bookingService.CreateBooking(name, flightNumber);
        return result ? (IActionResult) StatusCode((int)HttpStatusCode.Created) : StatusCode((int)HttpStatusCode.InternalServerError, exception.Message);
    }
    return StatusCode((int) HttpStatusCode.InternalServerError);
}
2. 验收测试和Swagger中间件

验证代码是否按预期工作有多种方法,这里我们介绍验收测试。验收测试主要有两种方式:
1. 使用 FlyTomorrow 提供的 OpenAPI 规范手动测试端点。
2. 添加可选的 Swagger 中间件以动态生成 OpenAPI 规范,并将其与提供的规范进行比较。

2.1 手动验收测试

在进行手动测试之前,我们需要制定一个测试方法和步骤:
1. 确定输入要求。
2. 确定成功路径和非数据库异常情况。
3. 进行测试!

需要测试的端点有:
- GET /flight
- GET /flight/{flightNumber}
- POST /booking/{flightNumber}

以下是各端点的测试情况:
| 端点 | 输入 | 预期返回状态 | 返回数据 |
| — | — | — | — |
| GET /flight | 无 | 200(含航班数据)、404、500 | 航班信息 |
| GET /flight/{flightNumber} | 有效航班号、无效航班号、数据库中不存在的有效航班号 | 200(含数据)、400、404 | 航班信息或错误信息 |
| POST /booking/{flightNumber} | 有效JSON数据、空字符串姓名JSON数据、缺少属性JSON数据、无效航班号、数据库中不存在的航班号 | 201、500 | 成功信息或错误信息 |

graph TD;
    A[开始测试] --> B[确定输入要求];
    B --> C[确定成功路径和异常情况];
    C --> D[测试GET /flight];
    C --> E[测试GET /flight/{flightNumber}];
    C --> F[测试POST /booking/{flightNumber}];
    D --> G{结果是否符合预期};
    E --> G;
    F --> G;
    G -- 是 --> H[测试通过];
    G -- 否 --> I[测试失败];
2.2 动态生成OpenAPI规范

要添加 Swagger 中间件,我们需要安装第三方库 Swashbuckle.AspNetCore ,并在 Startup.cs 中进行配置:

class Startup
{
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();
        app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
        app.UseSwagger();
        app.UseSwaggerUI(swagger => swagger.SwaggerEndpoint("/swagger/v1/swagger.json", "Flying Dutchman Airlines"));
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddTransient(typeof(FlightService), typeof(FlightService));
        services.AddTransient(typeof(BookingService), typeof(BookingService));
        services.AddTransient(typeof(FlightRepository), typeof(FlightRepository));
        services.AddTransient(typeof(AirportRepository), typeof(AirportRepository));
        services.AddTransient(typeof(BookingRepository), typeof(BookingRepository));
        services.AddTransient(typeof(CustomerRepository), typeof(CustomerRepository));
        services.AddDbContext<FlyingDutchmanAirlinesContext>(ServiceLifeTime.Transient);
        services.AddTransient(typeof(FlyingDutchmanAirlinesContext), typeof(FlyingDutchmanAirlinesContext));
        services.AddSwaggerGen();
    }
}

启动服务后,我们可以导航到 [service]/swagger/v1/swagger 查看生成的 Swagger UI 。如果发现自动生成的规范缺少某些返回状态信息,我们可以添加方法属性来指定:

[HttpGet("{flightNumber}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetFlightByFlightNumber(int flightNumber)
{ 
    // 方法逻辑
}

最后,我们将生成的 OpenAPI 规范与 FlyTomorrow 提供的规范进行比较,确保各个端点的返回状态和信息一致。

通过以上步骤,我们完成了飞行预订服务的实现和测试,确保了代码的正确性和可靠性。

飞行预订服务的实现与测试

3. 各端点OpenAPI规范比较
3.1 GET /flight端点比较

GET /flight 端点是比较OpenAPI规范时相对简单的一个。它是一个GET请求,不接收请求体,可能返回的状态码有200(包含找到的航班数据)、404(未找到数据)和500(出现问题)。

通过对比 FlyTomorrow 提供的规范和自动生成的OpenAPI规范,我们发现自动生成的规范涵盖了所有可能的返回状态码,结合之前的手动测试,我们可以认为这个端点的实现是符合要求的,可以判定为完成。

规范来源 返回状态码 说明
FlyTomorrow规范 200、404、500 涵盖所有预期状态
自动生成规范 200、404、500 与FlyTomorrow规范一致
graph LR;
    A[FlyTomorrow规范] --> B{状态码是否一致};
    C[自动生成规范] --> B;
    B -- 是 --> D[端点实现符合要求];
    B -- 否 --> E[端点实现需调整];
3.2 GET /Flight/{flightNumber}端点比较

GET /Flight/{flightNumber} 端点与 GET /flight 端点类似,但引入了路径参数 flightNumber 。它可能返回的状态码有200(包含数据)、400(无效请求)和404(未找到航班)。

同样,我们将 FlyTomorrow 的规范和自动生成的规范进行对比,发现两者的返回状态码是一致的,这表明这个端点的实现也是比较理想的,可以继续进行下一个端点的检查。

规范来源 返回状态码 说明
FlyTomorrow规范 200、400、404 涵盖预期状态
自动生成规范 200、400、404 与FlyTomorrow规范一致
graph LR;
    F[FlyTomorrow规范] --> G{状态码是否一致};
    H[自动生成规范] --> G;
    G -- 是 --> I[端点实现符合要求];
    G -- 否 --> J[端点实现需调整];
3.3 POST /Booking/{flightNumber}端点比较

POST /Booking/{flightNumber} 端点结合了POST请求、请求体和路径参数,需要对输入的JSON数据进行反序列化和序列化处理。它可能返回的状态码有201(创建成功)和500(内部服务器错误)。

在比较 FlyTomorrow 的规范和自动生成的规范时,我们要确保各个方面都一致,包括请求体的格式、返回状态码等。经过检查,如果两者一致,那么说明这个端点的实现也是符合要求的。

规范来源 返回状态码 说明
FlyTomorrow规范 201、500 涵盖预期状态
自动生成规范 201、500 与FlyTomorrow规范一致
graph LR;
    K[FlyTomorrow规范] --> L{状态码及请求体等是否一致};
    M[自动生成规范] --> L;
    L -- 是 --> N[端点实现符合要求];
    L -- 否 --> O[端点实现需调整];
4. 总结

通过一系列的操作,我们完成了飞行预订服务的实现与测试工作。具体步骤如下:
1. 实现 CreateBooking 端点方法逻辑,包括数据绑定验证、服务和存储库层的调用、参数处理等。
- 验证 BookingData 实例的有效性,实现 IValidatableObject 接口。
- 添加 BookingService 的支持字段和注入实例,配置依赖注入中间件。
- 处理 flightNumber 参数,调用 BookingService.CreateBooking 方法,并根据返回结果返回相应的HTTP状态码。
2. 进行验收测试,采用手动测试和添加 Swagger 中间件动态生成 OpenAPI 规范两种方式。
- 手动测试时,确定各端点的输入要求、成功路径和异常情况,对 GET /flight GET /flight/{flightNumber} POST /booking/{flightNumber} 端点进行测试。
- 添加 Swagger 中间件,安装 Swashbuckle.AspNetCore 库,在 Startup.cs 中配置,若自动生成的规范缺少返回状态信息,添加方法属性指定。
3. 比较各端点的 OpenAPI 规范,将自动生成的规范与 FlyTomorrow 提供的规范进行对比,确保各个端点的返回状态和信息一致。

通过这些步骤,我们确保了飞行预订服务代码的正确性和可靠性,为后续的应用和扩展奠定了坚实的基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值