Completed
Push — develop ( b1df34...10b422 )
by Jace
8s
created

TestStandard.test_function_to_json()   F

Complexity

Conditions 11

Size

Total Lines 51

Duplication

Lines 51
Ratio 100 %
Metric Value
dl 51
loc 51
rs 3.6
cc 11

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like TestStandard.test_function_to_json() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""Integration tests for the package."""
0 ignored issues
show
Duplication introduced by
Similar lines in 2 files
==tests.test_examples:51
==tests.test_files:33
@yorm.sync("path/to/self.category/self.name.yml")
class SampleStandardDecorated:
"""Sample class using standard attribute types."""

def __init__(self, name, category='default'):
self.name = name
self.category = category
# https://docs.python.org/3.4/library/json.html#json.JSONDecoder
self.object = {}
self.array = []
self.string = ""
self.number_int = 0
self.number_real = 0.0
Loading history...
Duplication introduced by
Similar lines in 2 files
==tests.test_examples:179
==tests.test_ordering:29
sample.string = "Hello, world!"
sample.number_int = 42
sample.number_real = 4.2
sample.truthy = False
sample.falsey = True
Loading history...
2
# pylint: disable=missing-docstring,no-self-use,no-member,misplaced-comparison-constant,attribute-defined-outside-init
0 ignored issues
show
introduced by
Bad option value 'misplaced-comparison-constant'
Loading history...
3
4
import logging
5
6
import yorm
7
from yorm.types import Object, String, Integer, Float, Boolean
8
from yorm.types import Markdown, Dictionary, List
9
10
from . import strip, refresh_file_modification_times
11
12
log = logging.getLogger(__name__)
13
14
15
# CLASSES ######################################################################
16
17
18
class EmptyDictionary(Dictionary):
19
    """Sample dictionary container."""
20
21
22
@yorm.attr(all=Integer)
23
class IntegerList(List):
24
    """Sample list container."""
25
26
27
class SampleStandard:
28
    """Sample class using standard attribute types."""
29
30
    def __init__(self):
31
        # https://docs.python.org/3.4/library/json.html#json.JSONDecoder
32
        self.object = {}
33
        self.array = []
34
        self.string = ""
35
        self.number_int = 0
36
        self.number_real = 0.0
37
        self.truthy = True
38
        self.falsey = False
39
        self.null = None
40
41
    def __repr__(self):
42
        return "<standard {}>".format(id(self))
43
44
45 View Code Duplication
@yorm.attr(array=IntegerList)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
46
@yorm.attr(falsey=Boolean)
47
@yorm.attr(number_int=Integer)
48
@yorm.attr(number_real=Float)
49
@yorm.attr(object=EmptyDictionary)
50
@yorm.attr(string=String)
51
@yorm.attr(truthy=Boolean)
52
@yorm.sync("path/to/{self.category}/{self.name}.yml")
53
class SampleStandardDecorated:
54
    """Sample class using standard attribute types."""
55
56
    def __init__(self, name, category='default'):
57
        self.name = name
58
        self.category = category
59
        # https://docs.python.org/3.4/library/json.html#json.JSONDecoder
60
        self.object = {}
61
        self.array = []
62
        self.string = ""
63
        self.number_int = 0
64
        self.number_real = 0.0
65
        self.truthy = True
66
        self.falsey = False
67
        self.null = None
68
69
    def __repr__(self):
70
        return "<decorated {}>".format(id(self))
71
72
73
@yorm.attr(label=String)
74
@yorm.attr(status=Boolean)
75
class StatusDictionary(Dictionary):
76
    """Sample dictionary container."""
77
78
79
@yorm.attr(all=StatusDictionary)
80
class StatusDictionaryList(List):
81
    """Sample list container."""
82
83
84
class Level(String):
85
    """Sample custom attribute."""
86
87
    @classmethod
88
    def to_data(cls, obj):
89
        value = cls.to_value(obj)
90
        count = value.split('.')
91
        if count == 0:
92
            return int(value)
93
        elif count == 1:
94
            return float(value)
95
        else:
96
            return value
97
98
99
@yorm.sync("path/to/directory/{UUID}.yml", attrs={'level': Level})
100
class SampleCustomDecorated:
101
    """Sample class using custom attribute types."""
102
103
    def __init__(self, name):
104
        self.name = name
105
        self.level = '1.0'
106
107
    def __repr__(self):
108
        return "<custom {}>".format(id(self))
109
110
111
@yorm.attr(string=String)
112
@yorm.sync("sample.yml", auto_save=False)
113
class SampleDecoratedAutoOff:
114
    """Sample class with automatic storage turned off."""
115
116
    def __init__(self):
117
        self.string = ""
118
119
    def __repr__(self):
120
        return "<auto save off {}>".format(id(self))
121
122
123
@yorm.sync("sample.yml", auto_attr=True)
124
class SampleEmptyDecorated:
125
    """Sample class using standard attribute types."""
126
127
    def __repr__(self):
128
        return "<empty {}>".format(id(self))
129
130
131
class SampleExtended:
132
    """Sample class using extended attribute types."""
133
134
    def __init__(self):
135
        self.text = ""
136
137
    def __repr__(self):
138
        return "<extended {}>".format(id(self))
139
140
141
class SampleNested:
142
    """Sample class using nested attribute types."""
143
144
    def __init__(self):
145
        self.count = 0
146
        self.results = []
147
148
    def __repr__(self):
149
        return "<nested {}>".format(id(self))
150
151
# TESTS ########################################################################
152
153
154
class TestStandard:
155
    """Integration tests for standard attribute types."""
156
157
    @yorm.attr(status=yorm.types.Boolean)
158
    class StatusDictionary(Dictionary):
159
        pass
160
161
    def test_decorator(self, tmpdir):
162
        """Verify standard attribute types dump/load correctly (decorator)."""
163
        tmpdir.chdir()
164
        sample = SampleStandardDecorated('sample')
165
        assert "path/to/default/sample.yml" == sample.__mapper__.path
166
167
        log.info("Checking object default values...")
168
        assert {} == sample.object
169
        assert [] == sample.array
170
        assert "" == sample.string
171
        assert 0 == sample.number_int
172
        assert 0.0 == sample.number_real
173
        assert True is sample.truthy
174
        assert False is sample.falsey
175
        assert None is sample.null
176
177
        log.info("Changing object values...")
178
        sample.object = {'key2': 'value'}
179
        sample.array = [0, 1, 2]
180
        sample.string = "Hello, world!"
181
        sample.number_int = 42
182
        sample.number_real = 4.2
183
        sample.truthy = False
184
        sample.falsey = True
185
186
        log.info("Checking file contents...")
187
        assert strip("""
188
        array:
189
        - 0
190
        - 1
191
        - 2
192
        falsey: true
193
        number_int: 42
194
        number_real: 4.2
195
        object: {}
196
        string: Hello, world!
197
        truthy: false
198
        """) == sample.__mapper__.text
199
200
        log.info("Changing file contents...")
201
        refresh_file_modification_times()
202
        sample.__mapper__.text = strip("""
203
        array: [4, 5, 6]
204
        falsey: null
205
        number_int: 42
206
        number_real: '4.2'
207
        object: {'status': false}
208
        string: "abc"
209
        truthy: null
210
        """)
211
212
        log.info("Checking object values...")
213
        assert {'status': False} == sample.object
214
        assert [4, 5, 6] == sample.array
215
        assert "abc" == sample.string
216
        assert 42 == sample.number_int
217
        assert 4.2 == sample.number_real
218
        assert False is sample.truthy
219
        assert False is sample.falsey
220
221 View Code Duplication
    def test_function(self, tmpdir):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
222
        """Verify standard attribute types dump/load correctly (function)."""
223
        tmpdir.chdir()
224
        _sample = SampleStandard()
225
        attrs = {'object': self.StatusDictionary,
226
                 'array': IntegerList,
227
                 'string': String,
228
                 'number_int': Integer,
229
                 'number_real': Float,
230
                 'truthy': Boolean,
231
                 'falsey': Boolean}
232
        sample = yorm.sync(_sample, "path/to/directory/sample.yml", attrs)
233
        assert "path/to/directory/sample.yml" == sample.__mapper__.path
234
235
        # check defaults
236
        assert {'status': False} == sample.object
237
        assert [] == sample.array
238
        assert "" == sample.string
239
        assert 0 == sample.number_int
240
        assert 0.0 == sample.number_real
241
        assert True is sample.truthy
242
        assert False is sample.falsey
243
        assert None is sample.null
244
245
        # change object values
246
        sample.object = {'key': 'value'}
247
        sample.array = [1, 2, 3]
248
        sample.string = "Hello, world!"
249
        sample.number_int = 42
250
        sample.number_real = 4.2
251
        sample.truthy = None
252
        sample.falsey = 1
253
254
        # check file values
255
        assert strip("""
256
        array:
257
        - 1
258
        - 2
259
        - 3
260
        falsey: true
261
        number_int: 42
262
        number_real: 4.2
263
        object:
264
          status: false
265
        string: Hello, world!
266
        truthy: false
267
        """) == sample.__mapper__.text
268
269 View Code Duplication
    def test_function_to_json(self, tmpdir):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
270
        """Verify standard attribute types dump/load correctly (function)."""
271
        tmpdir.chdir()
272
        _sample = SampleStandard()
273
        attrs = {'object': self.StatusDictionary,
274
                 'array': IntegerList,
275
                 'string': String,
276
                 'number_int': Integer,
277
                 'number_real': Float,
278
                 'truthy': Boolean,
279
                 'falsey': Boolean}
280
        sample = yorm.sync(_sample, "path/to/directory/sample.json", attrs)
281
        assert "path/to/directory/sample.json" == sample.__mapper__.path
282
283
        # check defaults
284
        assert {'status': False} == sample.object
285
        assert [] == sample.array
286
        assert "" == sample.string
287
        assert 0 == sample.number_int
288
        assert 0.0 == sample.number_real
289
        assert True is sample.truthy
290
        assert False is sample.falsey
291
        assert None is sample.null
292
293
        # change object values
294
        sample.object = {'key': 'value'}
295
        sample.array = [1, 2, 3]
296
        sample.string = "Hello, world!"
297
        sample.number_int = 42
298
        sample.number_real = 4.2
299
        sample.truthy = None
300
        sample.falsey = 1
301
302
        # check file values
303
        assert strip("""
304
        {
305
            "array": [
306
                1,
307
                2,
308
                3
309
            ],
310
            "falsey": true,
311
            "number_int": 42,
312
            "number_real": 4.2,
313
            "object": {
314
                "status": false
315
            },
316
            "string": "Hello, world!",
317
            "truthy": false
318
        }
319
        """, tabs=2, end='') == sample.__mapper__.text
320
321
    def test_auto_off(self, tmpdir):
322
        """Verify file updates are disabled with auto save off."""
323
        tmpdir.chdir()
324
        sample = SampleDecoratedAutoOff()
325
326
        sample.string = "hello"
327
        assert "" == sample.__mapper__.text
328
329
        sample.__mapper__.auto_store = True
330
        sample.string = "world"
331
332
        assert strip("""
333
        string: world
334
        """) == sample.__mapper__.text
335
336
337
class TestContainers:
338
    """Integration tests for attribute containers."""
339
340
    def test_nesting(self, tmpdir):
341
        """Verify standard attribute types can be nested."""
342
        tmpdir.chdir()
343
        _sample = SampleNested()
344
        attrs = {'count': Integer,
345
                 'results': StatusDictionaryList}
346
        sample = yorm.sync(_sample, "sample.yml", attrs, auto_attr=True)
347
348
        # check defaults
349
        assert 0 == sample.count
350
        assert [] == sample.results
351
352
        # change object values
353
        sample.count = 5
354
        sample.results = [{'status': False, 'label': "abc"},
355
                          {'status': None, 'label': None},
356
                          {'label': "def"},
357
                          {'status': True},
358
                          {}]
359
360
        # check file values
361
        assert strip("""
362
        count: 5
363
        results:
364
        - label: abc
365
          status: false
366
        - label: ''
367
          status: false
368
        - label: def
369
          status: false
370
        - label: ''
371
          status: true
372
        - label: ''
373
          status: false
374
        """) == sample.__mapper__.text
375
376
        # change file values
377
        refresh_file_modification_times()
378
        sample.__mapper__.text = strip("""
379
        count: 3
380
        other: 4.2
381
        results:
382
        - label: abc
383
        - label: null
384
          status: false
385
        - status: true
386
        """)
387
388
        # check object values
389
        assert 3 == sample.count
390
        assert 4.2 == sample.other
391
        assert [{'label': 'abc', 'status': False},
392
                {'label': '', 'status': False},
393
                {'label': '', 'status': True}] == sample.results
394
395
    def test_objects(self, tmpdir):
396
        """Verify containers are treated as objects when added."""
397
        tmpdir.chdir()
398
        sample = SampleEmptyDecorated()
399
400
        # change file values
401
        refresh_file_modification_times()
402
        sample.__mapper__.text = strip("""
403
        object: {'key': 'value'}
404
        array: [1, '2', '3.0']
405
        """)
406
407
        # (a mapped attribute must be read first to trigger retrieving)
408
        sample.__mapper__.fetch()
409
410
        # check object values
411
        assert {'key': 'value'} == sample.object
412
        assert [1, '2', '3.0'] == sample.array
413
414
        # check object types
415
        assert Object == sample.__mapper__.attrs['object']
416
        assert Object == sample.__mapper__.attrs['array']
417
418
419
class TestExtended:
420
    """Integration tests for extended attribute types."""
421
422
    def test_function(self, tmpdir):
423
        """Verify extended attribute types dump/load correctly."""
424
        tmpdir.chdir()
425
        _sample = SampleExtended()
426
        attrs = {'text': Markdown}
427
        sample = yorm.sync(_sample, "path/to/directory/sample.yml", attrs)
428
429
        # check defaults
430
        assert "" == sample.text
431
432
        # change object values
433
        refresh_file_modification_times()
434
        sample.text = strip("""
435
        This is the first sentence. This is the second sentence.
436
        This is the third sentence.
437
        """)
438
439
        # check file values
440
        assert strip("""
441
        text: |
442
          This is the first sentence.
443
          This is the second sentence.
444
          This is the third sentence.
445
        """) == sample.__mapper__.text
446
447
        # change file values
448
        refresh_file_modification_times()
449
        sample.__mapper__.text = strip("""
450
        text: |
451
          This is a
452
          sentence.
453
        """)
454
455
        # check object values
456
        assert "This is a sentence." == sample.text
457
458
459
class TestCustom:
460
    """Integration tests for custom attribute types."""
461
462
    def test_decorator(self, tmpdir):
463
        """Verify custom attribute types dump/load correctly."""
464
        tmpdir.chdir()
465
        sample = SampleCustomDecorated('sample')
466
467
        # check defaults
468
        assert '1.0' == sample.level
469
470
        # change values
471
        sample.level = '1.2.3'
472
473
        # check file values
474
        assert strip("""
475
        level: 1.2.3
476
        """) == sample.__mapper__.text
477
478
        # change file values
479
        refresh_file_modification_times()
480
        sample.__mapper__.text = strip("""
481
        level: 1
482
        """)
483
484
        # check object values
485
        assert '1' == sample.level
486