Skip to content

Commit dabf1a1

Browse files
committed
Add unit tests
1 parent 8c03242 commit dabf1a1

File tree

3 files changed

+201
-24
lines changed

3 files changed

+201
-24
lines changed

mlblocks/mlpipeline.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,9 @@ def _get_str_output(self, output):
209209

210210
for variable in block.produce_output:
211211
if variable['name'] == variable_name:
212-
return [{'name': variable_name, 'variable': output}]
212+
output_variable = deepcopy(variable)
213+
output_variable['variable'] = output
214+
return [output_variable]
213215

214216
raise ValueError('Block {} has no output {}'.format(block_name, variable_name))
215217

tests/features/test_partial_outputs.py

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import numpy as np
55

6-
from mlblocks.datasets import load_iris
76
from mlblocks.mlpipeline import MLPipeline
87

98

@@ -15,6 +14,7 @@ def almost_equal(obj1, obj2):
1514
for key, value in obj1.items():
1615
if key not in obj2:
1716
raise AssertionError("{} not in {}".format(key, obj2))
17+
1818
almost_equal(value, obj2[key])
1919

2020
else:
@@ -23,9 +23,14 @@ def almost_equal(obj1, obj2):
2323

2424
class TestPartialOutputs(TestCase):
2525
def setUp(self):
26-
dataset = load_iris()
27-
28-
self.X_train, self.X_test, self.y_train, self.y_test = dataset.get_splits(1)
26+
self.X = np.array([
27+
[1, 0, 0, 0, 0],
28+
[0, 1, 0, 0, 0],
29+
[0, 0, 1, 0, 0],
30+
[0, 0, 0, 1, 0],
31+
[0, 0, 0, 0, 1],
32+
])
33+
self.y = np.array([0, 0, 0, 0, 1])
2934

3035
def test_fit_output(self):
3136

@@ -36,6 +41,8 @@ def test_fit_output(self):
3641
]
3742
pipeline = MLPipeline(primitives)
3843

44+
named = 'default'
45+
list_ = ['default', 0]
3946
int_block = 0
4047
invalid_int = 10
4148
str_block = 'sklearn.preprocessing.StandardScaler#1'
@@ -44,34 +51,44 @@ def test_fit_output(self):
4451
invalid_variable = 'sklearn.preprocessing.StandardScaler#1.invalid'
4552

4653
# Run
47-
int_out = pipeline.fit(self.X_train[0:5], self.y_train[0:5], output_=int_block)
48-
str_out = pipeline.fit(self.X_train[0:5], self.y_train[0:5], output_=str_block)
49-
str_out_variable = pipeline.fit(self.X_train[0:5], self.y_train[0:5],
54+
named_out = pipeline.fit(self.X, self.y, output_=named)
55+
list_out = pipeline.fit(self.X, self.y, output_=list_)
56+
int_out = pipeline.fit(self.X, self.y, output_=int_block)
57+
str_out = pipeline.fit(self.X, self.y, output_=str_block)
58+
str_out_variable = pipeline.fit(self.X, self.y,
5059
output_=str_block_variable)
51-
no_output = pipeline.fit(self.X_train, self.y_train)
60+
no_output = pipeline.fit(self.X, self.y)
5261

5362
# Assert successful calls
5463
X = np.array([
55-
[0.71269665, -1.45152899, 0.55344946, 0.31740553],
56-
[0.26726124, 1.23648766, -1.1557327, -1.0932857],
57-
[-1.95991577, 0.967686, -1.1557327, -1.0932857],
58-
[0.71269665, -0.645124, 0.39067021, 0.31740553],
59-
[0.26726124, -0.10752067, 1.36734573, 1.55176035]
64+
[2., -0.5, -0.5, -0.5, -0.5],
65+
[-0.5, 2., -0.5, -0.5, -0.5],
66+
[-0.5, -0.5, 2., -0.5, -0.5],
67+
[-0.5, -0.5, -0.5, 2., -0.5],
68+
[-0.5, -0.5, -0.5, -0.5, 2.],
6069
])
70+
y = np.array([
71+
0, 0, 0, 0, 1
72+
])
73+
74+
almost_equal(named_out, y)
75+
assert len(list_out) == 2
76+
almost_equal(list_out[0], y)
77+
almost_equal(list_out[1], X)
6178
almost_equal(X, int_out)
6279
almost_equal(X, str_out)
6380
almost_equal(X, str_out_variable)
6481
assert no_output is None
6582

6683
# Run asserting exceptions
6784
with self.assertRaises(IndexError):
68-
pipeline.fit(self.X_train[0:5], self.y_train[0:5], output_=invalid_int)
85+
pipeline.fit(self.X, self.y, output_=invalid_int)
6986

7087
with self.assertRaises(ValueError):
71-
pipeline.fit(self.X_train[0:5], self.y_train[0:5], output_=invalid_block)
88+
pipeline.fit(self.X, self.y, output_=invalid_block)
7289

7390
with self.assertRaises(ValueError):
74-
pipeline.fit(self.X_train[0:5], self.y_train[0:5], output_=invalid_variable)
91+
pipeline.fit(self.X, self.y, output_=invalid_variable)
7592

7693
def test_fit_start(self):
7794
# Setup variables
@@ -87,8 +104,8 @@ def test_fit_start(self):
87104

88105
# Run first block
89106
context = {
90-
'X': self.X_train,
91-
'y': self.y_train
107+
'X': self.X,
108+
'y': self.y
92109
}
93110
int_start = 1
94111
str_start = 'sklearn.linear_model.LogisticRegression#1'
@@ -106,15 +123,15 @@ def test_predict_start(self):
106123
'sklearn.linear_model.LogisticRegression'
107124
]
108125
pipeline = MLPipeline(primitives)
109-
pipeline.fit(self.X_train, self.y_train)
126+
pipeline.fit(self.X, self.y)
110127

111128
# Mock the first block
112129
block_mock = Mock()
113130
pipeline.blocks['sklearn.preprocessing.StandardScaler#1'] = block_mock
114131

115132
# Run first block
116133
context = {
117-
'X': self.X_train,
134+
'X': self.X,
118135
}
119136
int_start = 1
120137
str_start = 'sklearn.linear_model.LogisticRegression#1'

tests/test_mlpipeline.py

Lines changed: 161 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def test_get_tunable_hyperparameters(self):
107107
@patch('mlblocks.mlpipeline.MLBlock', new=MagicMock())
108108
def test_get_tunable_hyperparameters_flat(self):
109109
mlpipeline = MLPipeline(['a_primitive'])
110-
tunable = {
110+
mlpipeline._tunable_hyperparameters = {
111111
'block_1': {
112112
'hp_1': {
113113
'type': 'int',
@@ -133,7 +133,6 @@ def test_get_tunable_hyperparameters_flat(self):
133133
}
134134
}
135135
}
136-
mlpipeline._tunable_hyperparameters = tunable
137136

138137
returned = mlpipeline.get_tunable_hyperparameters(flat=True)
139138

@@ -299,9 +298,168 @@ def test__get_block_args(self):
299298
}
300299
assert args == expected
301300

302-
def test__get_outputs(self):
301+
@patch('mlblocks.mlpipeline.MLBlock', new=MagicMock())
302+
def test__get_outputs_no_outputs(self):
303+
self_ = Mock()
304+
self_._last_block_name = 'last_block'
305+
self_._get_block_outputs.return_value = ['some', 'outputs']
306+
307+
pipeline = dict()
308+
outputs = None
309+
returned = MLPipeline._get_outputs(self_, pipeline, outputs)
310+
311+
expected = {
312+
'default': ['some', 'outputs']
313+
}
314+
assert returned == expected
315+
316+
self_._get_block_outputs.assert_called_once_with('last_block')
317+
318+
@patch('mlblocks.mlpipeline.MLBlock', new=MagicMock())
319+
def test__get_outputs_defaults(self):
320+
self_ = Mock()
321+
322+
pipeline = dict()
323+
outputs = {
324+
'default': ['some', 'outputs']
325+
}
326+
returned = MLPipeline._get_outputs(self_, pipeline, outputs)
327+
328+
expected = {
329+
'default': ['some', 'outputs']
330+
}
331+
assert returned == expected
332+
self_._get_block_outputs.assert_not_called()
333+
334+
@patch('mlblocks.mlpipeline.MLBlock', new=MagicMock())
335+
def test__get_outputs_additional(self):
336+
self_ = Mock()
337+
338+
pipeline = {
339+
'outputs': {
340+
'default': ['some', 'outputs'],
341+
'additional': ['other', 'outputs']
342+
}
343+
}
344+
outputs = None
345+
returned = MLPipeline._get_outputs(self_, pipeline, outputs)
346+
347+
expected = {
348+
'default': ['some', 'outputs'],
349+
'additional': ['other', 'outputs']
350+
}
351+
assert returned == expected
352+
self_._get_block_outputs.assert_not_called()
353+
354+
def test_get_outputs_str(self):
355+
pass
356+
357+
def test_get_outputs_int(self):
358+
pass
359+
360+
def test_get_outputs_list_of_str(self):
361+
pass
362+
363+
def test_get_outputs_list_of_int(self):
303364
pass
304365

366+
def test_get_outputs_named_outputs(self):
367+
pass
368+
369+
def test_get_outputs_combination(self):
370+
pass
371+
372+
@patch('mlblocks.mlpipeline.MLBlock')
373+
def test_get_outputs_invalid(self, mlblock_mock):
374+
outputs = {
375+
'default': [
376+
{
377+
'name': 'a_name',
378+
'variable': 'a_variable',
379+
'type': 'a_type',
380+
}
381+
],
382+
'debug': [
383+
{
384+
'name': 'another_name',
385+
'variable': 'another_variable',
386+
}
387+
]
388+
}
389+
mlblock_mock.side_effect = [MagicMock(), MagicMock()]
390+
pipeline = MLPipeline(['a_primitive', 'another_primitive'], outputs=outputs)
391+
392+
pipeline.blocks['a_primitive#1'].produce_output = [
393+
{
394+
'name': 'output',
395+
'type': 'whatever'
396+
}
397+
]
398+
pipeline.blocks['another_primitive#1'].produce_output = [
399+
{
400+
'name': 'something',
401+
}
402+
]
403+
404+
returned = pipeline.get_outputs(['default', 'debug', -1, 'a_primitive#1.output'])
405+
406+
expected = [
407+
{
408+
'name': 'a_name',
409+
'variable': 'a_variable',
410+
'type': 'a_type'
411+
},
412+
{
413+
'name': 'another_name',
414+
'variable': 'another_variable',
415+
},
416+
{
417+
'name': 'something',
418+
'variable': 'another_primitive#1.something',
419+
},
420+
{
421+
'name': 'output',
422+
'type': 'whatever',
423+
'variable': 'a_primitive#1.output'
424+
}
425+
]
426+
427+
assert returned == expected
428+
429+
@patch('mlblocks.mlpipeline.MLBlock', new=MagicMock())
430+
def test_get_output_names(self):
431+
outputs = {
432+
'default': [
433+
{
434+
'name': 'a_name',
435+
'variable': 'a_variable',
436+
'type': 'a_type',
437+
}
438+
]
439+
}
440+
pipeline = MLPipeline(['a_primitive'], outputs=outputs)
441+
442+
names = pipeline.get_output_names()
443+
444+
assert names == ['a_name']
445+
446+
@patch('mlblocks.mlpipeline.MLBlock', new=MagicMock())
447+
def test_get_output_variables(self):
448+
outputs = {
449+
'default': [
450+
{
451+
'name': 'a_name',
452+
'variable': 'a_variable',
453+
'type': 'a_type',
454+
}
455+
]
456+
}
457+
pipeline = MLPipeline(['a_primitive'], outputs=outputs)
458+
459+
names = pipeline.get_output_variables()
460+
461+
assert names == ['a_variable']
462+
305463
def test_fit(self):
306464
pass
307465

0 commit comments

Comments
 (0)