Airbyte Azure Blob Storage 连接器集成测试全解析:从 Source-Faker 数据生成到 Azurite 端到端验证
2026/9/23 6:05:07 网站建设 项目流程
  • 数据工程
  • 数据集成
  • ETL
  • 后端
  • 大数据

【免费下载链接】airbyte

Open-source data movement for ELT pipelines and AI agents — from APIs, databases & files to warehouses, lakes, and AI applications. Both self-hosted and Cloud.

项目地址:https://gitcode.com/gh_mirrors/ai/airbyte
点击查看免费下载

本指南以airbyte-integrations/connectors/source-azure-blob-storage/integration_tests/目录下的测试体系为核心,系统讲解 Airbyte Azure Blob Storage 连接器集成测试的完整链路:如何利用 Source-Faker 生成海量随机数据、如何借助 Azurite 模拟 Azure Blob 存储服务、以及如何针对 CSV / JSONL / Parquet / Avro 四种文件格式执行参数化读取测试并断言记录数。读完本文,你将掌握该连接器集成测试的目录结构、每个环节的底层实现(含源码级证据)以及完整的测试配置写法,能够独立读懂、复现并扩展这套端到端测试方案。

集成测试概览:一套基于容器的端到端验证方案

Azure Blob Storage 连接器的集成测试不依赖真实的 Azure 云资源,而是通过Azurite(微软官方提供的 Azure 存储服务模拟器)在本地 Docker 容器中搭建一套完整的测试环境。整条测试流水线可归纳为以下四个阶段(摘自 integration_tests/README.md 中的测试套件流程定义):

各阶段职责如下:

  • Generate Random CSV Files:以子进程方式调用 csv_export/main.sh,等待 3 个 CSV 文件(users / purchases / products)生成完毕;
  • Docker Container Setup:在 localhost 上启动 Azurite server emulator 容器;
  • Convert & upload file:读取生成的.csv文件,将其转换为目标格式(CSV / JSONL / Parquet / Avro)后批量上传到 Azurite 容器;
  • Tests:使用提供的 catalog 从源读取数据,断言读取到的记录数量;
  • 每个测试结束后,所有上传的文件都会被删除,保证测试环境可重复。

前置条件:跑通测试前需要准备的三个要素

原文档明确列出了运行集成测试的三项前置条件,每一项都在仓库中有对应的实体文件:

1.build_customization.py:为测试镜像安装 Docker

测试需要启动 Azurite 容器,因此连接器的 base image 必须内置 Docker CLI。build_customization.py 通过pre_connector_install钩子在镜像构建阶段完成安装:先通过apt-get安装curljq,再借助官方安装脚本https://get.docker.com安装 Docker。这也是 Airbyte 连接器镜像定制机制(customization)的典型用法——测试环境需要的额外运行时依赖不写进连接器本体,而是通过该文件按需注入。

2.config-*.jsonconfigured_catalog.json:测试输入的双要件

  • 配置文件:位于 integration_tests/configs/ 下,按文件格式拆分为config_integration_csv.jsonconfig_integration_jsonl.jsonconfig_integration_parquet.jsonconfig_integration_avro.json四份,分别对应四种被测格式;
  • Catalog:位于 integration_tests/configured_catalog.json(会话级共用),同时 integration_tests/integration_configured_catalog/configured_catalog.json 提供另一份带 JSON Schema 的 catalog,用于 Avro 文件生成时的 schema 推导。

此外,integration_tests/abnormal_states/ 目录为 avro / csv / jsonl / jsonl_newlines / parquet 提供了异常状态样本,integration_tests/expected_records/ 则保存了各格式与各 CSV 解析变体(如csv_no_headercsv_skip_rowscsv_with_nullscsv_user_schema等)的预期记录文件,共同构成断言依据。

3. Source-Faker 配置:测试数据的生产者

测试数据由 Airbyte 的Source-Faker连接器生成,其配置文件位于 integration_tests/csv_export/secret_faker/secret_faker.json:

{ "count": 100000, "seed": 0, "parallelism": 2, "always_updated": false }

其中count属性表示每个文件生成的记录数seed用于固定随机种子以保障数据可复现,parallelism控制生成并发度。整个 CSV 生成工具集位于 integration_tests/csv_export/,其内部结构为:

  • configured_catalog/configured_catalog.json:指定需要输出的流(users / purchases / products);
  • main.sh:总入口,负责调度容器与子脚本;
  • purchases.sh/products.sh/users.sh:分别针对三个流的并行 CSV 落盘脚本。

测试数据生成:Source-Faker 到 CSV 的并行流水线

生成随机 CSV 的入口是 main.sh,其核心逻辑如下:

docker run --rm \ -v /tmp/csv/csv_export/secret_faker:/secrets \ -v /tmp/csv/csv_export/configured_catalog:/integration_tests \ airbyte/source-faker:latest read \ --config /secrets/secret_faker.json \ --catalog /integration_tests/configured_catalog.json \ | tee >(./purchases.sh) >(./products.sh) >(./users.sh) > /dev/null

脚本首先将自身所在目录拷贝到/tmp/csv,随后以容器方式运行airbyte/source-faker:latest,把secret_faker.json作为--configconfigured_catalog.json作为--catalog传入,执行read命令输出 Airbyte 消息流。关键技巧在于tee+ 进程替换(process substitution):输出被同时扇出到三个子脚本,每个流(users / purchases / products)并行地将 RECORDS 消息通过jq转换为 CSV 并写盘。原文档特别强调这种"按流并行处理 + 仅依赖命令行工具"的设计是为了保证处理速度。CSV 文件最终输出到/tmp/csv/目录,即users.csvpurchases.csvproducts.csv

该目录下的 csv_export/README.md 还说明了这套工具的通用价值:通过--config--state--catalog参数手动推进 sync,可以分块读取并存储输入数据,例如按 100GB 一批将 1TB 级别的 Faker 数据拆成多个 chunk 落地。

Azurite 模拟环境:集成测试的存储底座

容器启动方式

在 conftest.py 中,connector_setup_fixture是一个session 级别且 autouse的 fixture:它先调用generate_random_csv_with_source_faker()生成 CSV,再启动 Azurite 容器:

container = docker_client.containers.run( image="mcr.microsoft.com/azure-storage/azurite", command="azurite-blob --blobHost 0.0.0.0 -l /data --loose", name=f"azurite_integration_{uuid.uuid4().hex}", hostname="azurite", ports={10000: ("0.0.0.0", 10000), 10001: ("0.0.0.0", 10001), 10002: ("0.0.0.0", 10002)}, environment={"AZURITE_ACCOUNTS": "account1:key1"}, detach=True, )

要点说明:

  • 使用mcr.microsoft.com/azure-storage/azurite官方镜像,仅启动 blob 服务(azurite-blob);
  • 映射 10000(Blob)、10001(Queue)、10002(Table)三个端口;
  • 通过AZURITE_ACCOUNTS=account1:key1预置测试账号与密钥;
  • 容器启动后time.sleep(10)等待服务就绪,随后创建名为testcontainer的容器;
  • fixture 结束时执行container.kill()container.remove()完成清理。

仓库同时提供了 integration_tests/docker-compose.yaml,以 Compose 形式描述了同一套 Azurite 服务(含./data:/data数据卷挂载与UseDevelopmentStorage=true环境变量),方便在本地手工起停模拟环境进行调试。

客户端连接与 Docker IP 处理

conftest.py 中的get_container_client()使用BlobServiceClient(f"http://{docker_ip}:10000/account1", credential="key1")建立连接。其中docker_ip由 utils.py 的get_docker_ip()决定:当DOCKER_HOST为空或以unix://开头时返回127.0.0.1,否则从tcp://host:port形式的地址中解析出宿主机 IP。各格式的 fixture 还会把配置中的azure_blob_storage_endpoint里的localhost替换为该 IP,确保测试容器能正确访问到 Azurite。

值得一提的细节是:conftest.py 通过monkey patch覆写了 Azure SDK 的_format_shared_key_credential,使其固定返回{"account_key": "key1", "account_name": "account1"}。代码注释说明了原因——原始方法只处理localhost/127.0.0.1地址,在 Dagger 的global-docker-host网络模式下无法工作,这一改动保证了测试在 Airbyte CI 容器环境中的兼容性。

参数化测试:四种格式 × 三十个文件的记录数断言

测试主体

integration_test.py 定义了唯一的核心测试test_read_files,通过@pytest.mark.parametrizeconfig_csvconfig_jsonlconfig_parquetconfig_avro四个配置 fixture 进行参数化:

@pytest.mark.parametrize( "config", [ "config_csv", "config_jsonl", "config_parquet", "config_avro", ], ) def test_read_files(configured_catalog: ConfiguredAirbyteCatalog, config: Mapping[str, Any], request): """Read 2_001_000 records in 30 files""" config = request.getfixturevalue(config) source = SourceAzureBlobStorage( SourceAzureBlobStorageStreamReader(), spec_class=SourceAzureBlobStorageSpec, catalog=configured_catalog, config=config, state=None, cursor_cls=DefaultFileBasedCursor, ) output = read(source=source, config=config, catalog=configured_catalog) assert sum(x.state.sourceStats.recordCount for x in output.state_messages) == 2_001_000

从源码结构可以读出以下几点实现事实:

  • 测试通过 CDK 的airbyte_cdk.test.entrypoint_wrapper.read驱动连接器执行读取,而非直接调用流方法,因此覆盖的是从配置解析、catalog 匹配到文件读取的完整路径;
  • 连接器实例由SourceAzureBlobStorage组合SourceAzureBlobStorageStreamReader构建,cursor 使用DefaultFileBasedCursor
  • 断言依据是各 stream state 消息中的stateSourceStats.recordCount之和,预期为2,001,000 条记录、30 个文件(代码注释明确标注)。

四种格式的转换与上传

每个 config fixture 的职责是:加载对应格式的配置、修正 endpoint 地址、上传 30 个文件(3 张表 × 每种格式 10 份拷贝)、测试结束后遍历删除全部 blob。以 CSV 为例:

def upload_csv_files(container_client: ContainerClient) -> None: """upload 30 csv files""" for table in ("products", "purchases", "users"): csv_large_file = open(f"/tmp/csv/{table}.csv", "rb").read() for i in range(10): container_client.upload_blob(f"test_csv_{table}_{i}.csv", csv_large_file, validate_content=False)

其余三种格式的转换路径各有特点:

  • JSONL:用 pandasread_csv读取 CSV 后,通过df.to_json(..., orient="records", lines=True)转换为 JSON Lines 格式;
  • Parquet:同样先read_csv,再df.to_parquet()直接序列化;
  • Avro:最为复杂——先从 catalog 中取出各流的 JSON Schema,映射为 Avro schema(JSON_TO_AVRO_TYPES = {"string": "string", "integer": "long", "number": "float", "object": "record"},并处理可空联合类型与默认值),再用fastavroparse_schema+writer写盘。

所有上传的 blob 命名遵循test_<format>_<table>_<i>.<ext>模式,与各配置文件中 stream 的globs模式一一对应(例如 CSV 流使用**/test_csv_users*.csv),这也是 glob 匹配规则能够在测试中精准命中目标文件的原因。

清理机制

每个 config fixture 在yield之后都会执行同样的收尾逻辑:

for blob in container_client.list_blobs(): container_client.delete_blob(blob.name)

即测试结束后遍历容器并删除所有已上传的 blob,确保不同格式的测试之间互不干扰、环境保持干净。

配置文件参考:四种格式的完整参数说明

四种格式的配置共享同一套连接器级参数,区别集中在streamsfile_typeglobsformat块。以下分别给出要点:

连接器级公共参数

参数示例值说明
azure_blob_storage_endpointhttp://localhost:10000/account1Blob 服务端点,集成测试指向 Azurite
azure_blob_storage_account_nameaccount1存储账号名,与AZURITE_ACCOUNTS对应
credentials.auth_typestorage_account_key认证方式(测试采用账号密钥)
azure_blob_storage_account_keykey1账号密钥
azure_blob_storage_container_nametestcontainer目标容器名

CSV 流完整示例

以 config_integration_csv.json 中的users流为例,它展示了 CSV 解析器的完整参数面:

{ "name": "users", "file_type": "csv", "globs": ["**/test_csv_users*.csv"], "legacy_prefix": "", "validation_policy": "Emit Record", "format": { "filetype": "csv", "delimiter": ",", "quote_char": "\"", "double_quote": true, "null_values": ["", "#N/A", "#N/A N/A", "#NA", "-1.#IND", "-1.#QNAN", "-NaN", "-nan", "1.#IND", "1.#QNAN", "N/A", "NA", "NULL", "NaN", "n/a", "nan", "null"], "true_values": ["1", "True", "TRUE", "true"], "false_values": ["0", "False", "FALSE", "false"], "inference_type": "Primitive Types Only", "strings_can_be_null": false, "encoding": "utf8", "header_definition": { "header_definition_type": "From CSV" } } }

参数含义:

  • delimiter/quote_char/double_quote:分隔符、引用符及引号内双写转义;
  • null_values/true_values/false_values:空值、布尔真/假值的字符串识别集合(含多种大小写与 Excel/NumPy 常见占位符);
  • inference_type:类型推断策略(测试使用Primitive Types Only);
  • strings_can_be_null:字符串字段是否允许为 null;
  • encoding:文件编码(utf8);
  • header_definition.header_definition_type:表头来源(From CSV,即从文件首行读取)。

purchasesproducts流结构完全一致,仅nameglobs不同。

JSONL / Parquet / Avro 流

  • JSONL(config_integration_jsonl.json):file_typejsonlformat仅需{"filetype": "jsonl"},并额外支持流级参数newlines_in_values: true(允许值内包含换行符);
  • Parquet(config_integration_parquet.json)与Avro(config_integration_avro.json):file_type分别为parquet/avroformat块同样只需声明filetype,因为列式格式自带 schema,无需额外解析参数。

值得注意的是,Parquet / Avro 配置中的流file_type字段写的是jsonl(见 config_integration_parquet.json 第 11 行等处),这属于测试配置中的既有取值,真正决定解析格式的是format.filetype。读者在参考这些配置编写自己的测试时,应以format.filetype为准并保持两者一致。

如何运行这套集成测试

结合 acceptance.py(声明connector_acceptance_test.plugin插件,并预留了 session 级外部依赖 setup 钩子)与上述源码,运行链路可归纳为:

  1. 确保 Docker 可用(集成测试会动态拉起 Azurite 容器),本地 Docker daemon 可通过 UNIX socket 或DOCKER_HOST=tcp://...访问;
  2. 通过连接器的测试入口(如 Airbyte CI 的connector-tests/ pytest 对integration_tests/目录的执行)运行测试,此时connector_setup_fixture会自动完成 CSV 生成、Azurite 启动、容器创建三个初始化动作;
  3. 四个参数化的测试用例依次执行:每个用例先上传对应格式的 30 个文件,再驱动SourceAzureBlobStorage按 catalog 读取,最后断言recordCount总和为 2,001,000;
  4. 每个用例结束即删除全部 blob,会话结束则销毁 Azurite 容器,环境完全自清理。

这套方案的价值在于:它用完全本地的模拟环境(Azurite)覆盖了真实存储服务的关键行为,同时以 Source-Faker 保证了数据规模与随机性,使得连接器对不同文件格式、glob 匹配、schema 推断与记录数统计的正确性能够得到稳定、可重复、无需云资源即可验证的保障。无论是排查连接器读取问题、新增文件格式支持,还是回归验证 catalog 变更,这套集成测试框架都是可以直接复用与扩展的基准。

  • 数据工程
  • 数据集成
  • ETL
  • 后端
  • 大数据

【免费下载链接】airbyte

Open-source data movement for ELT pipelines and AI agents — from APIs, databases & files to warehouses, lakes, and AI applications. Both self-hosted and Cloud.

项目地址:https://gitcode.com/gh_mirrors/ai/airbyte
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询