如何在pytest中根据条件选择数据库后端Fixture?
问题:根据条件动态选择SQLite/PostgreSQL测试后端
背景
我有一个基于SQLAlchemy构建的ETL项目,测试时通过pytest-docker使用Docker容器中的Postgres。但部分机器启动Docker容器耗时较长,因此我们也提供了SQLite后端。
需求是:通过pytest-docker提供的docker_ip和docker_services Fixture,根据条件选择任一后端,且无需启动Docker容器。
现有Fixture代码
我已经编写了以下几个Fixture:
@pytest.fixture(name="sqlite_db", scope="session") def fixture_sqlite_db(): # ... 初始化SQLite引擎的逻辑 yield engine @pytest.fixture(name="postgres_db", scope="session") def fixture_postgres_db(docker_ip, docker_services): # ... 初始化Postgres引擎的逻辑(依赖docker_ip和docker_services) yield engine @pytest.fixture(name="mock_get_engine", scope="session") def fixture_mock_get_engine(engine): with pytest.MonkeyPatch.context() as mock: mock.setattr(my_module, "get_engine", lambda: engine) yield mock
当前的(不理想的)实现方式
目前我把测试用例复制了两份,分别放在独立的测试文件中:
- Postgres测试文件开头添加了环境变量判断,未设置时直接跳过:
if not os.getenv("TEST_POSTGRES", False): pytest.skip("skipping postgres database tests", allow_module_level=True)
这种方式能正常运行,但复制半套测试套件的做法非常不优雅。
无效的尝试
我曾尝试直接在Fixture里做条件判断,但这种方法行不通:
@pytest.fixture(name="engine", scope="module") def fixture_engine(): if os.getenv("TEST_POSTGRES", False): engine = fixture_postgres_db() else: engine = fixture_sqlite_db() yield engine
正确解决方案:使用request.getfixturevalue
借助pytest内置的request Fixture可以实现条件选择Fixture,核心是用request.getfixturevalue()动态获取指定Fixture的实例:
@pytest.fixture(name="engine", scope="module") def fixture_engine(request): if os.getenv("TEST_POSTGRES", False): engine = request.getfixturevalue("postgres_db") else: engine = request.getfixturevalue("sqlite_db") yield engine
这样就不需要复制测试用例了,所有测试都可以复用同一个engine Fixture,通过环境变量TEST_POSTGRES控制后端选择:
- 设置
TEST_POSTGRES=1运行测试时,会自动触发postgres_dbFixture(启动Docker容器) - 不设置该环境变量时,默认使用
sqlite_dbFixture,不会启动Docker
内容的提问来源于stack exchange,提问作者Colombo
相关产品推荐
相关产品推荐

