88from os import path
99import sklearn
1010from sklearn .model_selection import train_test_split
11+ import pytest
12+ import math
1113
1214
1315def random_data_constructor (noise_mag = 1.0 ):
16+ """
17+ Random data constructor utility for tests
18+ """
1419 num_points = 100
1520 X = 10 * np .random .random (size = num_points )
1621 y = 2 * X + 3 + 2 * noise_mag * np .random .normal (size = num_points )
@@ -19,6 +24,9 @@ def random_data_constructor(noise_mag=1.0):
1924#-------------------------------------------------------------------
2025
2126def fixed_data_constructor ():
27+ """
28+ Fixed data constructor utility for tests
29+ """
2230 num_points = 100
2331 X = np .linspace (1 ,10 ,num_points )
2432 y = 2 * X + 3
@@ -27,11 +35,14 @@ def fixed_data_constructor():
2735#-------------------------------------------------------------------
2836
2937def test_model_return_object ():
38+ """
39+ Tests the returned object of the modeling function
40+ """
3041 X ,y = random_data_constructor ()
3142 scores = train_linear_model (X ,y )
3243
3344 #=================================
34- # TEST SUITES
45+ # TEST SUITE
3546 #=================================
3647 # Check the return object type
3748 assert isinstance (scores , dict )
@@ -43,11 +54,14 @@ def test_model_return_object():
4354#-------------------------------------------------------------------
4455
4556def test_model_return_vals ():
57+ """
58+ Tests for the returned values of the modeling function
59+ """
4660 X ,y = random_data_constructor ()
4761 scores = train_linear_model (X ,y )
4862
4963 #=================================
50- # TEST SUITES
64+ # TEST SUITE
5165 #=================================
5266 # Check returned scores' type
5367 assert isinstance (scores ['Train-score' ], float )
@@ -61,12 +75,15 @@ def test_model_return_vals():
6175#-------------------------------------------------------------------
6276
6377def test_model_save_load ():
78+ """
79+ Tests for the model saving process
80+ """
6481 X ,y = random_data_constructor ()
6582 filename = 'testing'
6683 _ = train_linear_model (X ,y , filename = filename )
6784
6885 #=================================
69- # TEST SUITES
86+ # TEST SUITE
7087 #=================================
7188 # Check the model file is created/saved in the directory
7289 assert path .exists ('testing.sav' )
@@ -78,6 +95,9 @@ def test_model_save_load():
7895#-------------------------------------------------------------------
7996
8097def test_loaded_model_works ():
98+ """
99+ Tests if the loading of the model works correctly
100+ """
81101 X ,y = fixed_data_constructor ()
82102 if len (X .shape ) == 1 :
83103 X = X .reshape (- 1 ,1 )
@@ -88,7 +108,7 @@ def test_loaded_model_works():
88108 loaded_model = load ('testing.sav' )
89109
90110 #=================================
91- # TEST SUITES
111+ # TEST SUITE
92112 #=================================
93113 # Check that test and train scores are perfectly equal to 1.0
94114 assert scores ['Train-score' ] == 1.0
@@ -101,6 +121,9 @@ def test_loaded_model_works():
101121#-------------------------------------------------------------------
102122
103123def test_model_works_data_range_sign_change ():
124+ """
125+ Tests for functionality with data scaled high and low
126+ """
104127 # Small-valued data
105128 X ,y = fixed_data_constructor ()
106129 X = 1.0e-9 * X
@@ -146,6 +169,9 @@ def test_model_works_data_range_sign_change():
146169#-------------------------------------------------------------------
147170
148171def test_noise_impact ():
172+ """
173+ Tests functionality with low and high noise data and expected change in the R^2 score
174+ """
149175 X ,y = random_data_constructor (noise_mag = 0.5 )
150176 filename = 'testing'
151177 scores_low_noise = train_linear_model (X ,y , filename = filename )
@@ -160,13 +186,45 @@ def test_noise_impact():
160186
161187#-------------------------------------------------------------------
162188
189+ def test_additive_invariance ():
190+ """
191+ Tests additive invariance
192+ i.e. adding constant numbers to X or y array does not change the model coefficients
193+ """
194+ X ,y = random_data_constructor (noise_mag = 0.5 )
195+ filename = 'testing'
196+
197+ _ = train_linear_model (X ,y , filename = filename )
198+ m = load ('testing.sav' )
199+ coeff_no_additive = float (m .coef_ )
200+
201+ X = X + 100
202+ _ = train_linear_model (X ,y , filename = filename )
203+ m = load ('testing.sav' )
204+ coeff_X_additive = float (m .coef_ )
205+
206+ y = y - 100
207+ _ = train_linear_model (X ,y , filename = filename )
208+ m = load ('testing.sav' )
209+ coeff_y_additive = float (m .coef_ )
210+
211+ # Check that model coefficients for default and additive data are same (very close)
212+ # Note the use of math.isclose function
213+ assert math .isclose (coeff_no_additive , coeff_X_additive , rel_tol = 1e-6 )
214+ assert math .isclose (coeff_no_additive , coeff_y_additive , rel_tol = 1e-6 )
215+
216+ #-------------------------------------------------------------------
217+
163218def test_wrong_input_raises_assertion ():
219+ """
220+ Tests for various assertion cheks written in the modeling function
221+ """
164222 X ,y = random_data_constructor ()
165223 filename = 'testing'
166224 scores = train_linear_model (X ,y , filename = filename )
167225
168226 #=================================
169- # TEST SUITES
227+ # TEST SUITE
170228 #=================================
171229 # Test that it handles the case of: X is a string
172230 msg = train_linear_model ('X' ,y )
@@ -195,4 +253,31 @@ def test_wrong_input_raises_assertion():
195253 X = X .reshape (10 ,10 )
196254 msg = train_linear_model (X ,y , filename = 'testing' )
197255 assert isinstance (msg , AssertionError )
198- assert msg .args [0 ] == "Row numbers of X and y data must be identical"
256+ assert msg .args [0 ] == "Row numbers of X and y data must be identical"
257+
258+ #-------------------------------------------------------------------
259+
260+ def test_raised_exception ():
261+ """
262+ Tests for raised exception with pytest.raises context manager
263+ """
264+ # ValueError
265+ with pytest .raises (ValueError ):
266+ # Insert a np.nan into the X array
267+ X ,y = random_data_constructor ()
268+ X [1 ] = np .nan
269+ filename = 'testing'
270+ scores = train_linear_model (X ,y , filename = filename )
271+ # Insert a np.nan into the y array
272+ X ,y = random_data_constructor ()
273+ y [1 ] = np .nan
274+ filename = 'testing'
275+ scores = train_linear_model (X ,y , filename = filename )
276+
277+ with pytest .raises (ValueError ) as exception :
278+ # Insert a string into the X array
279+ X ,y = random_data_constructor ()
280+ X [1 ] = "A string"
281+ filename = 'testing'
282+ scores = train_linear_model (X ,y , filename = filename )
283+ assert "could not convert string to float" in str (exception .value )
0 commit comments