Passed
Pull Request — master (#40)
by Paolo
07:29 queued 44s
created

ExcelTemplateReaderTestCase.test_check_accuracies_issue()   A

Complexity

Conditions 1

Size

Total Lines 63
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 29
dl 0
loc 63
rs 9.184
c 0
b 0
f 0
cc 1
nop 2

How to fix   Long Method   

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:

1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Tue Jul  2 10:58:42 2019
5
6
@author: Paolo Cozzi <[email protected]>
7
"""
8
9
import types
10
from collections import defaultdict
11
from unittest.mock import patch, Mock
12
13
from django.test import TestCase
14
from django.core.management import call_command
15
16
from common.tests import WebSocketMixin
17
from image_app.tests.mixins import (
18
    DataSourceMixinTestCase, FileReaderMixinTestCase)
19
20
from ..helpers import ExcelTemplateReader, upload_template, TEMPLATE_COLUMNS
21
from .common import BaseExcelMixin
22
23
24
class ExcelTemplateReaderTestCase(
25
        FileReaderMixinTestCase, DataSourceMixinTestCase, BaseExcelMixin,
26
        TestCase):
27
    """Test excel class upload"""
28
29
    def setUp(self):
30
        # calling my base class setup
31
        super().setUp()
32
33
        self.maxDiff = None
34
35
        # crate a Excel Template object
36
        self.reader = ExcelTemplateReader()
37
38
        # get filenames for DataSourceMixinTestCase.dst_path
39
        self.reader.read_file(self.dst_path)
40
41
    def test_check_sheets(self):
42
        # test check sheets method
43
        status, not_found = self.reader.check_sheets()
44
45
        self.assertTrue(status)
46
        self.assertEqual(not_found, [])
47
48
        # override sheet names
49
        self.reader.sheet_names = []
50
51
        # check method
52
        status, not_found = self.reader.check_sheets()
53
54
        self.assertFalse(status)
55
        self.assertEqual(not_found, ['breed', 'animal', 'sample'])
56
57
    def test_check_columns(self):
58
        # test check sheets method
59
        status, not_found = self.reader.check_columns()
60
61
        self.assertTrue(status)
62
        self.assertIsInstance(not_found, defaultdict)
63
        self.assertEqual(len(not_found), 0)
64
65
    @patch('xlrd.open_workbook')
66
    def test_check_columns_issue(self, mock_open):
67
        """Test a file with columns issues"""
68
69
        # creating a mock excel book
70
        mock_book = Mock()
71
72
        # customizing the mock object
73
        mock_book.sheet_names.return_value = ['breed', 'animal', 'sample']
74
75
        # creating a mock sheet for breed
76
        breed_sheet = Mock()
77
        breed_sheet.nrows = 1
78
79
        # now setting rows to a fake sheet
80
        breed_sheet.row_values.return_value = []
81
82
        # creating a mock sheet for animal
83
        animal_sheet = Mock()
84
        animal_sheet.nrows = 1
85
86
        # now setting rows to a fake sheet
87
        animal_sheet.row_values.return_value = []
88
89
        # creating a mock sheet for sample
90
        sample_sheet = Mock()
91
        sample_sheet.nrows = 1
92
93
        # now setting rows to a fake sheet
94
        sample_sheet.row_values.return_value = []
95
96
        # finally setting sheet to the fabe excel object
97
        mock_book.sheet_by_name.side_effect = [
98
            breed_sheet, animal_sheet, sample_sheet]
99
100
        # returning the mock object when opening a workbook
101
        mock_open.return_value = mock_book
102
103
        # now calling methods
104
        reader = ExcelTemplateReader()
105
        reader.read_file("fake file")
106
107
        # create the reference error output
108
        reference = defaultdict(list)
109
        reference['breed'] += TEMPLATE_COLUMNS['breed']
110
        reference['animal'] += TEMPLATE_COLUMNS['animal']
111
        reference['sample'] += TEMPLATE_COLUMNS['sample']
112
113
        status, test = reader.check_columns()
114
115
        self.assertFalse(status)
116
        self.assertIsInstance(test, defaultdict)
117
        self.assertDictEqual(reference, test)
118
119
    def check_generator(self, records, length):
120
        self.assertIsInstance(records, types.GeneratorType)
121
        self.assertEqual(len(list(records)), length)
122
123
    def test_get_breed_records(self):
124
        """get_breed_records returns an iterator"""
125
126
        breeds = self.reader.get_breed_records()
127
        self.check_generator(breeds, 2)
128
129
    def test_get_animal_records(self):
130
        """get_animal_records returns an iterator"""
131
132
        animals = self.reader.get_animal_records()
133
        self.check_generator(animals, 3)
134
135
    def test_get_sample_records(self):
136
        """get_sample_records returns an iterator"""
137
138
        samples = self.reader.get_sample_records()
139
        self.check_generator(samples, 3)
140
141
    def test_check_accuracies(self):
142
        """Test check accuracies method"""
143
144
        check, not_found = self.reader.check_accuracies()
145
146
        self.assertTrue(check)
147
        self.assertEqual(len(not_found), 0)
148
149
    @patch('xlrd.open_workbook')
150
    def test_check_accuracies_issue(self, mock_open):
151
        """Checking issues with accuracy in excels data"""
152
153
        # creating a mock excel book
154
        mock_book = Mock()
155
156
        # customizing the mock object
157
        mock_book.sheet_names.return_value = ['breed', 'animal', 'sample']
158
159
        # creating a mock sheet for animal
160
        animal_sheet = Mock()
161
        animal_sheet.nrows = 2
162
163
        # creating a fake row of data
164
        fake_row = ["" for col in TEMPLATE_COLUMNS['animal']]
165
166
        # get birth location accuracy index
167
        accuracy_idx = TEMPLATE_COLUMNS['animal'].index(
168
            "Birth location accuracy")
169
170
        # set a fake accuracy item
171
        fake_row[accuracy_idx] = "Fake"
172
173
        # now setting rows to a fake sheet
174
        animal_sheet.row_values.side_effect = [
175
            TEMPLATE_COLUMNS['animal'],
176
            fake_row]
177
178
        # creating a mock sheet for sample
179
        sample_sheet = Mock()
180
        sample_sheet.nrows = 2
181
182
        # creating a fake row of data
183
        fake_row = ["" for col in TEMPLATE_COLUMNS['sample']]
184
185
        # get birth location accuracy index
186
        accuracy_idx = TEMPLATE_COLUMNS['sample'].index(
187
            "Collection place accuracy")
188
189
        # set a fake accuracy item
190
        fake_row[accuracy_idx] = "Fake"
191
192
        # now setting rows to a fake sheet
193
        sample_sheet.row_values.side_effect = [
194
            TEMPLATE_COLUMNS['sample'],
195
            fake_row]
196
197
        # finally setting sheet to the fabe excel object
198
        mock_book.sheet_by_name.side_effect = [animal_sheet, sample_sheet]
199
200
        # returning the mock object when opening a workbook
201
        mock_open.return_value = mock_book
202
203
        # now calling methods
204
        reader = ExcelTemplateReader()
205
        reader.read_file("fake file")
206
207
        # define the expected value
208
        reference = (False, set(["Fake"]))
209
        test = reader.check_accuracies()
210
211
        self.assertEqual(reference, test)
212
213
214
class ExcelMixin(DataSourceMixinTestCase, WebSocketMixin, BaseExcelMixin):
215
    """Common tests for Excel classes"""
216
217
    # define the method to upload data from. Since the function is now inside
218
    # a class it becomes a method, specifically a bound method and is supposed
219
    # to receive the self attribute by default. If we don't want to get the
220
    # self attribute, we have to declare function as a staticmetho
221
    # https://stackoverflow.com/a/35322635/4385116
222
    upload_method = staticmethod(upload_template)
223
224
    def test_upload_template(self):
225
        """Testing uploading and importing data from excel template to UID"""
226
227
        # test data loaded
228
        message = "Template import completed for submission"
229
        self.upload_datasource(message)
230
231
        # check async message called
232
        notification_message = (
233
            'Template import completed for submission: 1')
234
        validation_message = {
235
            'animals': 3, 'samples': 3,
236
            'animal_unkn': 3, 'sample_unkn': 3,
237
            'animal_issues': 0, 'sample_issues': 0}
238
239
        # check async message called using WebSocketMixin.check_message
240
        self.check_message('Loaded', notification_message, validation_message)
241
242
    def check_errors(self, my_check, message, notification_message):
243
        """Common stuff for error in excel template loading"""
244
245
        super().check_errors(my_check, message)
246
247
        # check async message called using WebSocketMixin.check_message
248
        self.check_message('Error', notification_message)
249
250
251
class UploadTemplateTestCase(ExcelMixin, TestCase):
252
    """Test uploading data for Template excel path"""
253
254
    @patch("excel.helpers.ExcelTemplateReader.check_species",
255
           return_value=[False, 'Rainbow trout'])
256
    def test_upload_crbanim_errors_with_species(self, my_check):
257
        """Testing importing with data into UID with errors in species"""
258
259
        message = "Some species are not loaded in UID database"
260
        notification_message = (
261
            'Error in importing data: Some species '
262
            'are not loaded in UID database: Rainbow '
263
            'trout')
264
265
        # check crbanim import fails
266
        self.check_errors(my_check, message, notification_message)
267
268
    @patch("excel.helpers.ExcelTemplateReader.check_sex",
269
           return_value=[False, 'unknown'])
270
    def test_upload_crbanim_errors_with_sex(self, my_check):
271
        """Testing importing with data into UID with errors"""
272
273
        message = "Not all Sex terms are loaded into database"
274
        notification_message = (
275
            'Error in importing data: Not all Sex '
276
            'terms are loaded into database: check '
277
            'for unknown in your dataset')
278
279
        # check crbanim import fails
280
        self.check_errors(my_check, message, notification_message)
281
282
    @patch("excel.helpers.ExcelTemplateReader.check_accuracies",
283
           return_value=(False, set(["Fake"])))
284
    def test_upload_crbanim_errors_with_accuracies(self, my_check):
285
        """Testing importing with data into UID with errors"""
286
287
        message = "Not all accuracy levels are defined in database"
288
        notification_message = (
289
            "Error in importing data: Not all accuracy "
290
            "levels are defined in database: check "
291
            "for {'Fake'} in your dataset")
292
293
        # check crbanim import fails
294
        self.check_errors(my_check, message, notification_message)
295
296
297
class ReloadTemplateTestCase(ExcelMixin, TestCase):
298
    """Simulate a template reload case. Load data as in
299
    UploadTemplateTestCase, then call test which reload the same data"""
300
301
    # override used fixtures
302
    fixtures = [
303
        'crbanim/auth',
304
        'excel/dictspecie',
305
        'excel/image_app',
306
        'excel/submission',
307
        'excel/speciesynonym'
308
    ]
309