From 3b6c7b06b7d17ad4daa6a84941d46655a92f778f Mon Sep 17 00:00:00 2001 From: Eduard Kerkhoven Date: Wed, 10 Jun 2026 07:02:16 +0200 Subject: [PATCH] Share the linear-chain INIT model fixture via tests/conftest.py test_init.py, test_init_build.py and test_init_solvers.py each built the same linear-chain INIT model (EX_A -> A -> B -> C -> D) independently, differing only in the model id and whether gene rules were attached. Move that construction into a new tests/conftest.py as linear_chain_model / linear_chain_model_with_genes fixtures; the three files now reuse it (test bodies unchanged). The bespoke _toy_ftinit_model stays local. No behaviour change. --- tests/conftest.py | 47 ++++++++++++++++++++++++++++++++++++++ tests/test_init.py | 22 ++++-------------- tests/test_init_build.py | 19 +++------------ tests/test_init_solvers.py | 20 ++-------------- 4 files changed, 56 insertions(+), 52 deletions(-) create mode 100644 tests/conftest.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..0969872 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,47 @@ +"""Shared pytest fixtures for the raven_python test-suite. + +Currently the linear-chain INIT model used by the tINIT/ftINIT scoring tests, +which several modules previously built independently and identically. +""" +import cobra +import pytest + + +def _linear_chain_model(*, with_genes: bool = False) -> cobra.Model: + """EX_A -> A -(r1)-> B -(r2)-> C -(r3)-> D. + + A is taken up via the reversible ``EX_A``; ``r1``/``r2`` are the productive path + and ``r3`` the dead-end branch the INIT/scoring tests penalise. With ``with_genes`` + the three internal reactions get gene rules ``g1``/``g2``/``g3``. + """ + m = cobra.Model("net") + A, B, C, D = ( + cobra.Metabolite(x, name=x[:-2], compartment="c") + for x in ("A_c", "B_c", "C_c", "D_c") + ) + m.add_metabolites([A, B, C, D]) + exa = cobra.Reaction("EX_A", lower_bound=-1000, upper_bound=1000) + exa.add_metabolites({A: -1}) # negative flux = uptake of A + r1 = cobra.Reaction("r1", lower_bound=0, upper_bound=1000) + r1.add_metabolites({A: -1, B: 1}) + r2 = cobra.Reaction("r2", lower_bound=0, upper_bound=1000) + r2.add_metabolites({B: -1, C: 1}) + r3 = cobra.Reaction("r3", lower_bound=0, upper_bound=1000) + r3.add_metabolites({C: -1, D: 1}) + m.add_reactions([exa, r1, r2, r3]) + if with_genes: + for rid, rule in (("r1", "g1"), ("r2", "g2"), ("r3", "g3")): + m.reactions.get_by_id(rid).gene_reaction_rule = rule + return m + + +@pytest.fixture +def linear_chain_model() -> cobra.Model: + """A fresh linear-chain INIT model (no gene rules).""" + return _linear_chain_model() + + +@pytest.fixture +def linear_chain_model_with_genes() -> cobra.Model: + """A fresh linear-chain INIT model with gene rules g1/g2/g3 on r1/r2/r3.""" + return _linear_chain_model(with_genes=True) diff --git a/tests/test_init.py b/tests/test_init.py index 9bfbc10..e62a807 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -11,24 +11,10 @@ def _met(mid): @pytest.fixture -def model(): - """EX_A -> A -(r1)-> B -(r2)-> C -(r3)-> D, with A uptake and excretion allowed. - - r1, r2 are good (positive score); r3 is bad (negative score). - """ - m = cobra.Model("net") - A, B, C, D = _met("A_c"), _met("B_c"), _met("C_c"), _met("D_c") - m.add_metabolites([A, B, C, D]) - exa = cobra.Reaction("EX_A", lower_bound=-1000, upper_bound=1000) - exa.add_metabolites({A: -1}) # negative flux = uptake of A - r1 = cobra.Reaction("r1", lower_bound=0, upper_bound=1000) - r1.add_metabolites({A: -1, B: 1}) - r2 = cobra.Reaction("r2", lower_bound=0, upper_bound=1000) - r2.add_metabolites({B: -1, C: 1}) - r3 = cobra.Reaction("r3", lower_bound=0, upper_bound=1000) - r3.add_metabolites({C: -1, D: 1}) - m.add_reactions([exa, r1, r2, r3]) - return m +def model(linear_chain_model): + # The linear-chain INIT model now lives in tests/conftest.py (it was built + # identically here, in test_init_build.py and test_init_solvers.py). + return linear_chain_model def test_keeps_positive_drops_negative(model): diff --git a/tests/test_init_build.py b/tests/test_init_build.py index cbc566f..d3fadc7 100644 --- a/tests/test_init_build.py +++ b/tests/test_init_build.py @@ -78,22 +78,9 @@ def test_expression_per_gene_reference(): # get_init_model pipeline # --------------------------------------------------------------------------- # @pytest.fixture -def model(): - m = cobra.Model("net") - A, B, C, D = (cobra.Metabolite(x, name=x[:-2], compartment="c") for x in ("A_c", "B_c", "C_c", "D_c")) - m.add_metabolites([A, B, C, D]) - exa = cobra.Reaction("EX_A", lower_bound=-1000, upper_bound=1000) - exa.add_metabolites({A: -1}) - r1 = cobra.Reaction("r1", lower_bound=0, upper_bound=1000) - r1.add_metabolites({A: -1, B: 1}) - r2 = cobra.Reaction("r2", lower_bound=0, upper_bound=1000) - r2.add_metabolites({B: -1, C: 1}) - r3 = cobra.Reaction("r3", lower_bound=0, upper_bound=1000) - r3.add_metabolites({C: -1, D: 1}) - m.add_reactions([exa, r1, r2, r3]) - for r, rule in (("r1", "g1"), ("r2", "g2"), ("r3", "g3")): - m.reactions.get_by_id(r).gene_reaction_rule = rule - return m +def model(linear_chain_model_with_genes): + # Shared linear-chain INIT model (with gene rules g1/g2/g3) — see tests/conftest.py. + return linear_chain_model_with_genes def test_get_init_model_from_gene_scores(model): diff --git a/tests/test_init_solvers.py b/tests/test_init_solvers.py index 514c408..7a034b4 100644 --- a/tests/test_init_solvers.py +++ b/tests/test_init_solvers.py @@ -66,22 +66,6 @@ def _met(mid, comp="c"): return cobra.Metabolite(mid, name=mid.split("_")[0], compartment=comp) -def _toy_init_model() -> cobra.Model: - """EX_A → A → B → C → D (r1, r2 good; r3 bad). Same network as test_init.py.""" - def rxn(rid, lb, ub, mets): - r = cobra.Reaction(rid, lower_bound=lb, upper_bound=ub) - r.add_metabolites(mets) - return r - m = cobra.Model("toy") - A, B, C, D = (_met(x) for x in ("A_c", "B_c", "C_c", "D_c")) - m.add_metabolites([A, B, C, D]) - m.add_reactions([rxn("EX_A", -1000, 1000, {A: -1}), - rxn("r1", 0, 1000, {A: -1, B: 1}), - rxn("r2", 0, 1000, {B: -1, C: 1}), - rxn("r3", 0, 1000, {C: -1, D: 1})]) - return m - - def _toy_ftinit_model() -> cobra.Model: """Small flux-consistent network for ftINIT: A→B, B→C, parallel A→C (negative-score).""" def rxn(rid, lb, ub, mets): @@ -101,9 +85,9 @@ def rxn(rid, lb, ub, mets): # --------------------------------------------------------------------- tests -def test_run_init_same_verdict(solver): +def test_run_init_same_verdict(solver, linear_chain_model): """tINIT MILP on a small network drops the negative-score reaction with any solver.""" - m = _toy_init_model() + m = linear_chain_model m.solver = solver res = run_init(m, {"r1": 1.0, "r2": 1.0, "r3": -1.0}, prod_weight=0.0, allow_excretion=True) assert "r3" in res.deleted_reactions