Oracle 到 PostgreSQL 迁移在 Phase 3 为 .NET 数据访问层编写可移植的 Oracle 基线集成测试【免费下载链接】awesome-copilotCommunity-contributed instructions, agents, skills, and configurations to help you make the most of GitHub Copilot.项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-copilot导读本文聚焦于 skills/creating-oracle-to-postgres-migration-integration-tests/SKILL.md 所定义的方法论在 Oracle 到 PostgreSQL 迁移的Phase 3 阶段为一个 .NET 目标项目的数据访问层编写集成测试。这套测试以Oracle 为权威基线golden source捕获 Oracle 的真实行为并刻意写成逻辑可移植——断言不依赖任何平台特有的错误消息或 SQL 语法从而保证在 Phase 6 迁移测试项目到 PostgreSQL 时断言代码无需任何改动即可复用用于验证迁移前后行为一致。读完本文你将掌握测试项目约定发现、数据访问工件盘点、种子数据设计、可移植断言编写与确定性审查的完整五步工作流以及空字符串、NO_DATA_FOUND、时间戳时区等关键行为差异下的断言技巧。迁移大图为什么为 Oracle 写测试是迁移的第一步在 skills/creating-oracle-to-postgres-master-migration-plan/SKILL.md 描述的迁移编排中整个迁移被拆分为多个阶段测试工作分布在两处Phase 3在 Oracle 仍是唯一目标库时为每个待迁移项目搭建测试基础设施并编写针对 Oracle 的基线集成测试。此时 PostgreSQL 迁移尚未开始。Phase 6将 Phase 3 建好的测试项目整体迁移到 PostgreSQL复制并改造为.Postgres项目此时测试仍能运行用于证明迁移后的 PostgreSQL 行为与 Oracle 基线一致。这套设计的核心假设是Oracle 的当前行为就是需求。迁移的目的不是改进业务逻辑而是让 PostgreSQL 复刻 Oracle 的可观察行为。因此测试必须以 Oracle 的输出为基准记录下来迁移后的 PostgreSQL 实现只要通过同一套测试就说明行为等价。这也就是 skills/planning-oracle-to-postgres-migration-integration-testing/SKILL.md 反复强调的 Oracle is the golden source。同时需要注意迁移后的应用是复制改名如MyApp.Postgres而非多库共存于同一进程因此不做多连接测试编排——每个测试实例只针对一个数据库参见 planning-oracle-to-postgres-migration-integration-testing/SKILL.md 的 No multi-connection harnessing 约束。前置条件在开始编写测试用例之前必须满足以下条件缺一不可测试项目已存在且可编译集成测试项目由 scaffolding-oracle-to-postgres-migration-test-project/SKILL.md 单独脚手架生成——它创建了 xUnit 测试项目、事务回滚基类base test class与种子数据管理器seed manager但不包含任何测试用例。本技能只在这样一个空壳上填充用例。先读约定再动手编写任何测试前必须先阅读现有的基类、种子数据管理器与项目文件理解继承模式、事务管理与种子文件约定而不是自行发明新约定。工作流总览五步完成 Oracle 基线集成测试Test Creation: - [ ] Step 1: Discover the test project conventions - [ ] Step 2: Identify testable data access artifacts - [ ] Step 3: Create seed data - [ ] Step 4: Write test cases - [ ] Step 5: Review determinism下面逐一步骤展开。Step 1发现测试项目约定目标在写任何测试之前先理解脚手架项目已经建立的约定确保新测试与之一致。需要阅读的资产包括基础测试类base test class理解继承关系。脚手架阶段会实现每个测试前开启事务、测试后回滚的基类新测试类必须继承它才能自动获得事务隔离。种子数据管理器seed manager理解种子数据的加载入口、文件位置与命名规范。种子文件命名规范在脚手架阶段确立本阶段必须沿用这样 Phase 6 迁移测试项目时无需重构。项目文件.csproj确认 .NET 版本、Oracle 连接包Oracle.ManagedDataAccess.Core与 xUnit 依赖理解运行环境。从 scaffolding-oracle-to-postgres-migration-test-project/SKILL.md 可以看到这套基础设施的原始设计意图基类在每个测试前开启事务、测试后回滚且要求捕获并处理所有异常以保证回滚必定发生种子数据管理器负责在事务作用域内加载测试数据明确禁止TRUNCATE TABLE以保护既有数据库数据测试项目只引用目标应用项目不引用解决方案中的其他应用项目确保测试范围收敛。Step 2识别可测的数据访问工件目标盘点目标项目内所有与数据库交互的代码路径形成测试覆盖清单。要点如下只限定目标项目不要为项目外的工件创建测试。列出所有与数据库交互的方法仓储repositories、DAO、存储过程调用方、查询构建器query builders都属于范围。这一步与 planning-oracle-to-postgres-migration-integration-testing/SKILL.md 的 Step 1 一脉相承——规划阶段会输出可测工件清单含方法签名与建议用例本阶段按图索骥逐项落实即可。规划文档还给出了风险优先级的排序思路优先覆盖使用 Oracle 特有特性的方法refcursor、TO_CHAR、隐式类型转换、NO_DATA_FOUND简单 CRUD 排后。Step 3创建种子数据种子数据是断言确定性的根基。本节每一条规则都直接影响测试的可重复性与 Phase 6 的可移植性遵循既有种子文件的位置与命名约定沿用 Step 1 从种子管理器发现的约定不要另起炉灶。避免TRUNCATE TABLE绝不清空既有表保持数据库中已有业务行与查找表lookup行原封不动。这是因为测试运行在共享数据库上清空数据会破坏其他测试与其他开发者的环境。只添加最小化、无冲突collision-safe的种子记录既然假定业务行与查找行已存在就只补足场景所需的最小记录集且键值要避免与既有数据冲突。不要提交种子数据测试运行在事务中结束时整体回滚种子数据不落库。这也意味着测试不能依赖上次运行遗留的副作用。确保种子数据不与其他测试冲突不同测试共享同一库时用唯一键/唯一标识隔离场景。断言前先加载并验证种子数据先加载、先校验再让断言依赖它避免因种子加载失败产生误导性的断言错误。建立或复用测试LookupConstants类把种子构建器与断言共用的查找表 ID/代码收敛为常量避免魔法数字散落各处也方便 Phase 6 后继续使用。Step 4编写测试用例本步骤是技能的核心规则可直接作为团队的测试编写规范继承基础测试类自动获得事务创建/回滚能力测试天然隔离、互不污染。覆盖率底线范围内每个触碰数据库的方法至少一个集成测试对风险更高的行为分支允许且鼓励多个测试。断言逻辑输出而非平台消息断言应落在行数、列值、计数、错误类型等逻辑层面绝不把 Oracle 特有的错误文案写进断言——否则 Phase 6 迁移到 PostgreSQL 时断言必然翻车。断言具体值种子数据能提供的具体值就断言具体值禁止仅仅断言非空或非空串。例如种子中已知status_code ACTIVE就断言等于ACTIVE而不是断言IsNotNull。不测不存在的路径不要为代码中不存在的行为分支编写测试也不要断言不可能发生的行为。避免冗余断言同一方法已被其他测试覆盖的行为不要重复断言保持测试意图单一。文本参数双覆盖对文本参数同时覆盖空字符串与NULL/缺失两种输入——这正是 Oracle 与 PostgreSQL 行为差异最大的雷区之一详见下文。datetime 读写一致性断言写入值与读回值一致且精度以Oracle 列的实际精度为准例如无小数秒的日期时间列按秒级验证不要假设任何特定数据库的类型语法。这样测试在迁移到 PostgreSQL 的timestamp列后依然成立。Step 5审查确定性目标逐条复查所有对非空值的断言确认其相对种子数据是确定的。对每个断言问一个问题这个结果是否完全由种子数据决定如果依赖了测试控制范围之外的数据库状态如同库其他测试写入的数据、系统时间、会话时区就把它修掉。常见的不确定来源包括依赖最大 ID或最新记录而未在种子中固定顺序、依赖NOW()/SYSDATE进行范围比较而未固定边界、依赖服务器时区设置。只有在所有断言都可复现后这一批测试才算完成。可移植断言的实战要点四大行为差异如何写断言本文档特别强调断言的逻辑可移植性。仓库中 skills/reviewing-oracle-to-postgres-migration 的参考资料详细记录了 Oracle→PostgreSQL 的行为差异这些差异正是基线测试必须钉住的验证点空字符串与 NULL必须双覆盖empty-strings-handling.md 指出Oracle 在VARCHAR2列中自动把空字符串视为NULL而 PostgreSQL 中两者是不同值。因此Oracle 中WHERE column 永远匹配不到行必须用IS NULLPostgreSQL 中WHERE column 匹配空串IS NULL匹配NULL互不干扰。断言层面建议采用对两种行为都成立的模式// 迁移兼容的读取与断言模式 var value reader.IsDBNull(columnIndex) ? null : reader.GetString(columnIndex); Assert.True(string.IsNullOrEmpty(value));这也解释了为什么 Step 4 要求文本参数必须同时覆盖与NULL——正是为了在 Phase 3 记录下 Oracle 的真实行为Phase 6 时据此判断 PostgreSQL 是否需要NULLIF(param, )之类的修正。SELECT INTO 无数据断言错误类型而非消息no-data-found-exceptions.md 记录了经典差异Oracle 的SELECT INTO无行时自动抛出ORA-01403: no data foundPostgreSQL 的SELECT INTOplpgsql无行时只是把FOUND置为false并静默继续。迁移后的 PostgreSQL 存储过程需要显式补上IF NOT FOUND THEN RAISE EXCEPTION no data found; END IF;因此Phase 3 的基线测试应断言传入非法参数时抛出异常类型而非断言具体的错误文本如ORA-01403。这样 Phase 6 迁移后只要 PostgreSQL 侧补上了等价的RAISE EXCEPTION同一断言无需改动即可通过。时间戳与时区以列精度为准、校验读写一致oracle-to-postgres-timestamp-timezone.md 详细对比了两者的时间戳行为Oracle 的CURRENT_TIMESTAMP受会话时区影响并按其列声明精度存储PostgreSQL 的CURRENT_TIMESTAMP/NOW()返回以 UTC 锚定的timestamptz且 Npgsql 驱动在不同版本/配置下返回的DateTime.Kind不同旧版或 legacy 模式为UnspecifiedNpgsql 6 默认为Utc。落到测试断言上本技能给出的规则是按 Oracle 列的精度断言写入值与读回值匹配例如秒级精度列就断言到秒并配合如下模式验证往返一致性[Fact] public async Task InsertedTimestamp_ShouldRoundTripAsUtc() { var before DateTime.UtcNow; await repository.InsertAuditEntryAsync(/* ... */); var retrieved await repository.GetLatestAuditEntryAsync(); Assert.Equal(DateTimeKind.Utc, retrieved.CreatedAt.Kind); Assert.True(retrieved.CreatedAt before, Persisted CreatedAt should not be earlier than the pre-insert UTC timestamp.); }这种记录 Oracle 行为、不写死平台语法的断言正是 oracle-to-postgres-timestamp-timezone.md 末尾检查清单中集成测试断言DateTime.Kind Utc的直接落地。NVL / DECODE 等 NULL 处理函数专测 NULL 输入oracle-nvl-decode-functions.md 指出Oracle 的NVL/NVL2/DECODE需替换为 PostgreSQL 的COALESCE/CASE其中DECODE把两个NULL视为相等不同于语义翻译成CASE时必须用IS NULL守卫-- Oracle: DECODE treats NULL NULL DECODE(col, NULL, empty, col) -- PostgreSQL CASE WHEN col IS NULL THEN empty ELSE col END该参考文档明确建议编写专门针对 NULL 输入的测试用例——NVL→COALESCE的翻译直白但DECODE→CASE的 NULL 相等性边界是最常见的静默行为差异来源。这为本文 Step 4 的文本参数双覆盖补充了一个具体的场景依据。关键约束本技能的边界与红线技能文档在末尾列出了不可逾越的约束理解这些约束有助于在团队中正确划定职责仅限 Phase 3这些测试只针对 Oracle。不要在 Phase 6 调用本技能也不要对已面向 PostgreSQL 的项目调用。Phase 6 的 PostgreSQL 测试项目来自对现有项目的迁移而不是重新生成。Oracle 是黄金来源测试捕获的是 Oracle 的期望行为迁移验证以此为标尺。断言可移植断言中不出现平台特有的错误消息或语法保证 Phase 6 迁移后断言零改动。种子只面向 Oracle种子数据与测试基础设施在 Phase 6 之前始终面向 Oracle不做任何 PostgreSQL 化。单项目范围只为目标项目内的工件创建测试不越界。保护既有数据绝不复写或清空既有的业务行与查找行——这与脚手架阶段禁止TRUNCATE TABLE的约束一脉相承。与同仓相关技能的协作闭环本技能不是孤立的它在整个迁移测试体系中的上下游关系如下均可在本仓库中找到对应文档creating-oracle-to-postgres-master-migration-plan/SKILL.md确定哪些项目需要 MIGRATE、迁移顺序并定义了 Phase 编号含 Phase 4 Schema/DDL 迁移、Phase 6 测试项目迁移。scaffolding-oracle-to-postgres-migration-test-project/SKILL.md前置步骤产出可编译的空 xUnit 测试项目、事务回滚基类与种子管理器——本技能的所有测试都建立在其上。planning-oracle-to-postgres-migration-integration-testing/SKILL.md规划测试范围与用例清单输出到.github/oracle-to-postgres-migration/Reports/{TARGET_PROJECT} Integration Testing Plan.md——本技能按计划逐项实现。migrating-oracle-to-postgres-data-access-code/SKILL.mdPhase 6 的数据访问代码迁移含NpgsqlDbType映射、refcursor 处理、NEXTVAL→nextval等迁移后的.Postgres项目复跑 Phase 3 的测试作为验收。结语一份可持续到 Phase 6 的测试资产为 Oracle 编写以具体值为断言、以逻辑输出为标尺、以列精度为时间基准的集成测试短期看是记录基线长期看是为 Phase 6 的迁移验收准备可复用的测试资产。五步工作流——发现约定、盘点工件、构造种子、编写用例、审查确定性——保证每一步都可复核而Oracle 是黄金来源 断言可移植两条铁律则确保这套测试在数据库切换后依然有效让迁移验证从人工核对升级为测试证明。【免费下载链接】awesome-copilotCommunity-contributed instructions, agents, skills, and configurations to help you make the most of GitHub Copilot.项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-copilot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考