飞行预订服务的实现与测试
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
提供的规范进行对比,确保各个端点的返回状态和信息一致。
通过这些步骤,我们确保了飞行预订服务代码的正确性和可靠性,为后续的应用和扩展奠定了坚实的基础。
飞行预订服务实现与测试
超级会员免费看

被折叠的 条评论
为什么被折叠?



