上个月,我在维护一套接口自动化测试框架时,遇到一个几乎让我抓狂的问题:测试用例跑着跑着就失败,而且失败得毫无规律。排查到最后,发现罪魁祸首是测试数据——硬编码的账号、订单号在并发执行时互相污染,用例之间数据耦合严重。这篇文章就记录我如何把测试数据管理从硬编码重构为数据工厂+随机数据的过程,希望能给同样被测试数据折磨的你一些启发。

场景与目标

当时的测试框架是用 Python + pytest 搭建的,用例数量大概 200 多条。最初写用例时,为了省事,我在代码里直接硬编码测试数据,比如:

def test_create_order(): payload = {"user_id": 1001, "product_id": 2002, "quantity": 3} resp = client.post("/orders", json=payload) assert resp.status_code == 201

这种写法的问题很明显:用例之间数据耦合,比如多个用例共用同一个 user_id,当并发执行时,一个用例可能修改了用户状态,另一个用例就失败了。而且数据不可复用,换一套环境就得改代码。

我的目标是:让测试数据与用例解耦,支持并发执行,且数据可复用。具体来说,希望达到以下效果:

  • 用例不再出现硬编码数据,数据由工厂动态生成。
  • 并发执行时,每个用例使用唯一数据,互不干扰。
  • 测试结束后,能自动清理产生的数据,避免脏数据堆积。

环境准备

我用的技术栈是 Python 3.10 + pytest + requests。为了生成随机数据,我引入了 Faker 库(一个生成假数据的 Python 库)。另外,数据库清理我用的是 SQLAlchemy 的 session 来执行删除语句,如果你用的是 Django ORM 或裸 SQL 也可以。

安装依赖:

pip install faker sqlalchemy

重构步骤

第一步,设计数据工厂。我创建了一个 data_factory.py 文件,用 Faker 生成随机但合理的数据。比如生成一个用户:

from faker import Fakerimport randomfake = Faker()class UserFactory: @staticmethod def create_user(): return { "username": fake.user_name(), "email": fake.email(), "phone": fake.phone_number(), "status": random.choice(["active", "inactive"]) }

第二步,在用例中使用工厂。重构后的创建订单用例变成:

def test_create_order(): user = UserFactory.create_user() # 先创建用户,拿到 user_id user_resp = client.post("/users", json=user) user_id = user_resp.json()["id"] payload = {"user_id": user_id, "product_id": 2002, "quantity": 3} resp = client.post("/orders", json=payload) assert resp.status_code == 201 # 记录清理所需的用户ID,可以在 teardown 中删除 cleanup_list.append(user_id)

注意,这里我故意没有用随机 quantity,而是固定值,因为业务规则可能限制数量范围。但用户数据是随机的,保证了唯一性。

第三步,设计数据库清理策略。我在 conftest.py 中实现了一个 autouse fixture,在每个用例执行后清理产生的数据。

import pytestfrom sqlalchemy import create_engine, textengine = create_engine("postgresql://user:pass@localhost/testdb")@pytest.fixture(autouse=True)def cleanup_data(): yield # 清理所有产生的用户数据 with engine.connect() as conn: conn.execute(text("DELETE FROM orders WHERE user_id IN (SELECT id FROM users WHERE username LIKE 'test_%')")) conn.execute(text("DELETE FROM users WHERE username LIKE 'test_%'")) conn.commit()

但这里有个问题:如果用户名不是以 test_ 开头,清理就失效了。所以我调整了工厂,让生成的用户名带上前缀:

def create_user(): return { "username": f"test_{fake.user_name()}", ... }

这样清理策略就统一了。

核心洞见:数据工厂的核心不是“随机”,而是“可控的随机”。通过给数据加前缀、按业务规则生成,既能保证唯一性,又能方便清理。

常见问题

1. 随机数据可能导致测试不稳定。比如生成手机号可能不符合运营商规则,我建议用 Faker 的 locale 参数,或者自定义 provider 来生成符合国内规则的手机号。

2. 清理策略不能仅仅依赖前缀。如果测试过程中创建了关联数据,需要级联删除。我的建议是在清理时先删子表,再删父表。

3. 并发测试时的数据冲突。即使随机,也可能碰撞。我用的是 uuid 作为部分字段,进一步降低冲突概率。

import uuid"username": f"test_{uuid.uuid4().hex[:8]}",

总结

重构后,我的测试框架跑得稳多了。并发执行时,用例之间不再互相干扰,数据也不会堆积。这个模式让我意识到,测试数据管理是自动化测试的基石,值得花时间去设计。

如果你也在做接口自动化,不妨试试数据工厂 + 随机数据 + 清理策略这套组合拳。如果你有更好的思路,欢迎交流。我们铭锦数智也常帮客户搭建自动化测试体系,有需要可以聊聊。