Skip to content
This repository was archived by the owner on Jan 21, 2026. It is now read-only.

Commit f5a0e92

Browse files
authored
#24 from achalbajpai/week-4
week 4 : implementing proper testing for our application
2 parents 35b5c19 + 22e3833 commit f5a0e92

8 files changed

Lines changed: 1974 additions & 154 deletions

src/peptide_calculator.py

Lines changed: 194 additions & 154 deletions
Large diffs are not rendered by default.

tests/conftest.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import pytest
2+
import sys
3+
import os
4+
from unittest.mock import MagicMock, patch
5+
6+
# Add project root to path for imports
7+
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
8+
sys.path.insert(0, PROJECT_ROOT)
9+
10+
11+
def pytest_configure(config):
12+
"""
13+
Configure pytest with custom markers for test categorization.
14+
These markers help categorize and run specific sets of tests.
15+
"""
16+
config.addinivalue_line("markers", "unit: Unit tests for individual functions")
17+
config.addinivalue_line("markers", "integration: Integration tests")
18+
config.addinivalue_line("markers", "validation: Validation function tests")
19+
config.addinivalue_line("markers", "parsing: Parsing function tests")
20+
config.addinivalue_line("markers", "modification: Modification handling tests")
21+
config.addinivalue_line("markers", "calculation: M/Z calculation tests")
22+
config.addinivalue_line("markers", "edge_case: Edge case tests")
23+
24+
25+
@pytest.fixture
26+
def mock_pyopenms():
27+
"""
28+
Mocks the pyOpenMS library to enable testing of
29+
peptide m/z calculation logic without requiring
30+
the actual pyOpenMS dependency to be installed.
31+
This fixture provides mock objects for AASequence and ModificationsDB
32+
with predefined return values for common methods, simulating
33+
the behavior of pyOpenMS for testing purposes.
34+
"""
35+
mock_poms = MagicMock()
36+
mock_poms.__version__ = "2.9.1"
37+
38+
mock_aa_seq = MagicMock()
39+
mock_aa_seq.getMZ.return_value = 500.25
40+
mock_aa_seq.getMonoWeight.return_value = 999.49
41+
mock_aa_seq.getFormula.return_value.toString.return_value = "C43H66N12O12"
42+
mock_poms.AASequence.fromString.return_value = mock_aa_seq
43+
44+
mods = {
45+
"Oxidation": {
46+
"id": "Oxidation",
47+
"full_name": "Oxidation",
48+
"mass": 15.994915,
49+
"specificity": 0,
50+
"origin": "M",
51+
},
52+
"Carbamidomethyl": {
53+
"id": "Carbamidomethyl",
54+
"full_name": "Carbamidomethyl",
55+
"mass": 57.021464,
56+
"specificity": 0,
57+
"origin": "C",
58+
},
59+
"Phospho": {
60+
"id": "Phospho",
61+
"full_name": "Phosphorylation",
62+
"mass": 79.966331,
63+
"specificity": 0,
64+
"origin": "STY",
65+
},
66+
"Acetyl": {
67+
"id": "Acetyl",
68+
"full_name": "Acetylation",
69+
"mass": 42.010565,
70+
"specificity": 1,
71+
"origin": "",
72+
},
73+
"Methyl": {
74+
"id": "Methyl",
75+
"full_name": "Methylation",
76+
"mass": 14.015650,
77+
"specificity": 0,
78+
"origin": "KR",
79+
},
80+
"Deamidated": {
81+
"id": "Deamidated",
82+
"full_name": "Deamidation",
83+
"mass": 0.984016,
84+
"specificity": 0,
85+
"origin": "NQ",
86+
},
87+
"Amidated": {
88+
"id": "Amidated",
89+
"full_name": "Amidation",
90+
"mass": -0.984016,
91+
"specificity": 2,
92+
"origin": "",
93+
},
94+
}
95+
96+
def get_mod_side_effect(mod_id):
97+
if mod_id in mods:
98+
mod_data = mods[mod_id]
99+
mock_mod = MagicMock()
100+
mock_mod.getId.return_value = mod_data["id"]
101+
mock_mod.getFullName.return_value = mod_data["full_name"]
102+
mock_mod.getDiffMonoMass.return_value = mod_data["mass"]
103+
mock_mod.getTermSpecificity.return_value = mod_data["specificity"]
104+
mock_mod.getOrigin.return_value = mod_data["origin"]
105+
return mock_mod
106+
raise ValueError(f"Mock modification not found: {mod_id}")
107+
108+
mock_mod_db = MagicMock()
109+
mock_mod_db.getModification.side_effect = get_mod_side_effect
110+
mock_poms.ModificationsDB.return_value = mock_mod_db
111+
112+
mock_poms.ResidueModification.ANYWHERE = 0
113+
mock_poms.ResidueModification.N_TERM = 1
114+
mock_poms.ResidueModification.C_TERM = 2
115+
116+
with patch.dict(sys.modules, {"pyopenms": mock_poms}):
117+
yield mock_poms
118+
119+
120+
@pytest.fixture
121+
def sample_peptides():
122+
"""
123+
Provides a dictionary of sample peptide sequences categorized
124+
by their characteristics (e.g., basic, with modifications, with charge, invalid).
125+
Useful for testing various peptide parsing and calculation scenarios.
126+
"""
127+
return {
128+
"basic": ["PEPTIDE", "SEQUENCE", "PROTEIN", "ANALYSIS"],
129+
"with_modifications": [
130+
"M[Oxidation]PEPTIDE",
131+
"C[Carbamidomethyl]PEPTIDE",
132+
"[Acetyl]PEPTIDE",
133+
"PEPTIDE[Amidated]",
134+
],
135+
"with_charge": ["PEPTIDE/2", "SEQUENCE/3", "PEPTIDE2", "SEQUENCE3"],
136+
"mass_deltas": [
137+
"M[+15.9949]PEPTIDE",
138+
"C[+57.021464]PEPTIDE",
139+
"PEPTIDE[+42.0106]",
140+
],
141+
"complex": [
142+
".[Acetyl]M[Oxidation]PEPTIDEC[Carbamidomethyl]/2",
143+
"EM[+15.9949]EVEES[-79.9663]PEK",
144+
"ALSSC[UNIMOD:4]VVDEEQDVER/2",
145+
],
146+
"invalid": ["PEPTIDEZ", "PEPTIDE123", "", " "],
147+
}
148+
149+
150+
@pytest.fixture
151+
def expected_modifications():
152+
"""
153+
Provides a dictionary of common peptide modifications and their
154+
expected monoisotopic mass deltas.
155+
Useful for validating modification parsing and mass calculations.
156+
"""
157+
return {
158+
"Oxidation (M)": 15.994915,
159+
"Carbamidomethyl (C)": 57.021464,
160+
"Phosphorylation (S/T/Y)": 79.966331,
161+
"Acetylation (N-term)": 42.010565,
162+
"Methylation (K/R)": 14.015650,
163+
"Deamidation (N/Q)": 0.984016,
164+
}

0 commit comments

Comments
 (0)