当前位置: 首页 > news >正文

(八)RestAPI 毛子(Unit Testing)


文章目录

  • 项目地址
  • 一、Unit Testing
    • 1.1 创建X unit 测试项目
      • 1. 创建项目目录
      • 2. 管理包
    • 1.2 创建CreateEntryDtoValidator测试
    • 1.3 创建CreateEntryDtoValidator测试
  • 二、Integration test
    • 2.1 创建Integration test环境
      • 1. 安装所需要的包
    • 2.2 配置基础设置
      • 1. 数据库链接DevHabitWebAppFactory
      • 2.创建测试共享类IntegrationTestCollection
      • 3. 注入登录用户
    • 2.3 添加测试用例
      • 1. 测试用户创建
      • 2. 测试添加Habits


项目地址

  • 教程作者:
  • 教程地址:
  • 代码仓库地址:
  • 所用到的框架和插件:
dbt 
airflow

一、Unit Testing

  • 适合单元测试的代码

在这里插入图片描述

1.1 创建X unit 测试项目

1. 创建项目目录

  • 创建测试
    在这里插入图片描述
  • 目录
    在这里插入图片描述

2. 管理包

  1. 修改Packages.props
    在这里插入图片描述
  2. 修改项目本身的包,并且添加项目引用到api项目
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><IsPackable>false</IsPackable></PropertyGroup><ItemGroup><PackageReference Include="coverlet.collector" /><PackageReference Include="Microsoft.NET.Test.Sdk" /><PackageReference Include="xunit" /><PackageReference Include="xunit.runner.visualstudio" /></ItemGroup><ItemGroup><ProjectReference Include="..\DevHabit\DevHabit.Api\DevHabit.Api.csproj" /></ItemGroup><ItemGroup><Using Include="Xunit" /></ItemGroup></Project>
  1. 在api的项目配置中添加,我们就可以引用internal 类型在Unitest里
    在这里插入图片描述

1.2 创建CreateEntryDtoValidator测试

  • 创建CreateEntryDtoValidatorTests用来测试
    在这里插入图片描述
namespace DevHabit.UnitTests.Validators;
public class CreateEntryDtoValidatorTests
{private readonly CreateEntryDtoValidator _validator = new();[Fact] // 用来定义不带参数的独立测试public async Task Validate_ShouldSucceed_WhenInputDtoIsValid(){// Arrangevar dto = new CreateEntryDto{HabitId = Habit.NewId(),Value = 1,Date = DateOnly.FromDateTime(DateTime.UtcNow)};// ActValidationResult validationResult = await _validator.ValidateAsync(dto);// AssertAssert.True(validationResult.IsValid);Assert.Empty(validationResult.Errors); //通过验证,错误列表为空}[Fact]public async Task Validate_ShouldFail_WhenHabitIdIsEmpty(){// Arrangevar dto = new CreateEntryDto{HabitId = string.Empty,Value = 1,Date = DateOnly.FromDateTime(DateTime.UtcNow)};// ActValidationResult validationResult = await _validator.ValidateAsync(dto);// AssertAssert.False(validationResult.IsValid); //验证失败ValidationFailure validationFailure = Assert.Single(validationResult.Errors); //检查错误列表里应该有且只有一个错误Assert.Equal(nameof(CreateEntryDto.HabitId), validationFailure.PropertyName); //验证这个错误是针对 HabitId 字段的}
}

1.3 创建CreateEntryDtoValidator测试

  • 为加密服务创建测试

public sealed class EncryptionServiceTests
{private readonly EncryptionService _encryptionService;public EncryptionServiceTests(){IOptions<EncryptionOptions> options = Options.Create(new EncryptionOptions{Key = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32))});_encryptionService = new EncryptionService(options);}[Fact]public void EncryptAndDecrypt_ShouldReturnOriginalPlainText(){// Arrangeconst string plainText = "sensitive data";string cipherText = _encryptionService.Encrypt(plainText);// Actstring decryptedText = _encryptionService.Decrypt(cipherText);// AssertAssert.Equal(plainText, decryptedText);}
}

二、Integration test

  • 用于测试一个用例或者

2.1 创建Integration test环境

1. 安装所需要的包

  • 用于测试api
    在这里插入图片描述
  • 用于测试数据库
    在这里插入图片描述

2.2 配置基础设置

在这里插入图片描述

1. 数据库链接DevHabitWebAppFactory

  • 测试时,应用只连接到 DevHabitWebAppFactory 启动的那个 Postgres 容器。测试结束后,DisposeAsync() 会自动停止并清理容器
namespace DevHabit.IntegrationTests.Infrastructure;
public sealed class DevHabitWebAppFactory : WebApplicationFactory<Program>, IAsyncLifetime
{private readonly PostgreSqlContainer _postgresContainer = new PostgreSqlBuilder().WithImage("postgres:17.2").WithDatabase("devhabit").WithUsername("postgres").WithPassword("postgres").Build();protected override void ConfigureWebHost(IWebHostBuilder builder){builder.UseSetting("ConnectionStrings:Database", _postgresContainer.GetConnectionString());}public async Task InitializeAsync(){await _postgresContainer.StartAsync();}public new async Task DisposeAsync(){await _postgresContainer.StopAsync();}
}

2.创建测试共享类IntegrationTestCollection

  • 让多个测试类能共享同一个 DevHabitWebAppFactory
namespace DevHabit.IntegrationTests.Infrastructure;//让多个测试类能共享同一个 DevHabitWebAppFactory
[CollectionDefinition(nameof(IntegrationTestCollection))]
public sealed class IntegrationTestCollection : ICollectionFixture<DevHabitWebAppFactory>;

3. 注入登录用户

  • 由于测试api需要登录用户,所以创建用户并且保存token
namespace DevHabit.IntegrationTests.Infrastructure;//IClassFixture整个测试类共享一个 WebAppFactory
public abstract class IntegrationTestFixture(DevHabitWebAppFactory factory) : IClassFixture<DevHabitWebAppFactory>
{//1.定义了一个缓存的 HttpClient,用于存储登录过的、有认证 token 的客户端private HttpClient? _authorizedClient;//2.创建一个普通的(未登录的)HttpClientpublic HttpClient CreateClient() => factory.CreateClient();//3.创建已经登录好的 HttpClient 的方法public async Task<HttpClient> CreateAuthenticatedClientAsync(string email = "test@test.com",string password = "Test123!"){if (_authorizedClient is not null){return _authorizedClient;}HttpClient client = CreateClient();// Check if user existsbool userExists;using (IServiceScope scope = factory.Services.CreateScope()){using ApplicationDbContext dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();userExists = await dbContext.Users.AnyAsync(u => u.Email == email);}if (!userExists){// Register a new userHttpResponseMessage registerResponse = await client.PostAsJsonAsync(Routes.Auth.Register,new RegisterUserDto{Email = email,Name = email,Password = password,ConfirmPassword = password});registerResponse.EnsureSuccessStatusCode();}// Login to get the tokenHttpResponseMessage loginResponse = await client.PostAsJsonAsync(Routes.Auth.Login,new LoginUserDto{Email = email,Password = password});loginResponse.EnsureSuccessStatusCode();AccessTokensDto? loginResult = await loginResponse.Content.ReadFromJsonAsync<AccessTokensDto>();if (loginResult?.AccessToken is null){throw new InvalidOperationException("Failed to get authentication token");}client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", loginResult.AccessToken);_authorizedClient = client;return client;}
}
  • Routes.cs:测试使用的路由
namespace DevHabit.IntegrationTests.Infrastructure;
public static class Routes
{public static class Auth{public const string Register = "auth/register";public const string Login = "auth/login";}public static class Habits{public const string Create = "habits";}
}

2.3 添加测试用例

1. 测试用户创建

  • 测试用户注册
public sealed class AuthenticationTests(DevHabitWebAppFactory factory) : IntegrationTestFixture(factory)
{[Fact]public async Task Register_ShouldSucceed_WithValidParameters(){// Arrangevar dto = new RegisterUserDto{Name = "register@test.com",Email = "register@test.com",Password = "Test123!",ConfirmPassword = "Test123!"};HttpClient client = CreateClient();// ActHttpResponseMessage response = await client.PostAsJsonAsync(Routes.Auth.Register, dto);// AssertAssert.Equal(HttpStatusCode.OK, response.StatusCode);}[Fact]public async Task Register_ShouldReturnAccessTokens_WithValidParameters(){// Arrangevar dto = new RegisterUserDto{Name = "register1@test.com",Email = "register1@test.com",Password = "Test123!",ConfirmPassword = "Test123!"};HttpClient client = CreateClient();// ActHttpResponseMessage response = await client.PostAsJsonAsync(Routes.Auth.Register, dto);response.EnsureSuccessStatusCode();// AssertAccessTokensDto? accessTokensDto = await response.Content.ReadFromJsonAsync<AccessTokensDto>();Assert.NotNull(accessTokensDto);}
}

2. 测试添加Habits

  • 测试创建CreateHabit
public sealed class HabitsTests(DevHabitWebAppFactory factory) : IntegrationTestFixture(factory)
{[Fact]public async Task CreateHabit_ShouldSucceed_WithValidParameters(){// Arrangevar dto = new CreateHabitDto{Name = "Read Books",Description = "Read technical books to improve skills",Type = HabitType.Measurable,Frequency = new FrequencyDto{Type = FrequencyType.Daily,TimesPerPeriod = 1},Target = new TargetDto{Value = 30,Unit = "pages"}};HttpClient client = await CreateAuthenticatedClientAsync();// ActHttpResponseMessage response = await client.PostAsJsonAsync(Routes.Habits.Create, dto);// AssertAssert.Equal(HttpStatusCode.Created, response.StatusCode);Assert.NotNull(response.Headers.Location);Assert.NotNull(await response.Content.ReadFromJsonAsync<HabitDto>());}
}

http://www.mrgr.cn/news/100566.html

相关文章:

  • 二极管钳位电路——Multisim电路仿真
  • Centos小白之在CentOS8.5中安装Rabbitmq 3.10.8
  • 【Keil5-开发指南】
  • 电路中的DGND、GROUND、GROUND_REF的区别,VREF、VCC、VDD、VEE和VSS的区别?
  • 在g2o中,顶点(Vertex)和边(Edge)插入到概率图的流程
  • 【Vue.js】组件数据通信:基于Props 实现父组件→子组件传递数据(最基础案例)
  • 练习普通话,说话更有节奏
  • day005
  • 架构风格对比
  • IEC 61850标准协议解读 2.基于Java的MMS实现
  • 怎么把Ubuntu系统虚拟环境中启动命令做成系统服务可以后台运行?
  • 安装qt4.8.7
  • QT6 源(58)篇一:阅读与注释 QString 这个类,先给出其应用举例
  • 通过深度学习推进增材制造:当前进展与未来挑战综述
  • 【wpf】 WPF中实现动态加载图片浏览器(边滚动边加载)
  • 庙算兵棋推演AI开发初探(7-神经网络训练与评估概述)
  • Nacos-3.0.0适配PostgreSQL数据库
  • Rust 学习笔记:关于切片的两个练习题
  • SNMP协议之详解(Detailed Explanation of SNMP Protocol)
  • 浅谈PCB传输线(一)