0

I have simple class with the public build method I want to test. Currently I assert all values it returns in every test. Is it a good practice or I should write one test for static values and in other tests only check values which change depending on input?

Implementation

class FiltersAttachment:
    TYPE_OPTIONS = [
        {"text": "All types", "value": "all"},
        {"text": ":link: Webpages", "value": "web_pages"}
    ]

    STATUS_OPTIONS = [
        {"text": "Available / Unavailable", "value": "all"},
        {"text": ":white_circle: Available", "value": "available"},
        {"text": ":red_circle: Unavailable", "value": "unavailable"}
    ]

    @classmethod
    def _filter_options(cls, options, selected):
        return list(filter(lambda t: t['value'] == selected, options))

    @classmethod
    def build(cls, check_type='', status=''):
        return {
            'fallback': 'Filters',
            'callback_id': 'resource_filters',
            'color': '#d2dde1',
            'mrkdwn_in': ['text'],
            'actions': [
                {
                    'name': 'resource_type',
                    'text': 'Type',
                    'type': 'select',
                    'options': cls.TYPE_OPTIONS,
                    'selected_options': cls._filter_options(
                        cls.TYPE_OPTIONS, check_type)
                },
                {

                    'name': 'resource_status',
                    'text': 'Status',
                    'type': 'select',
                    'options': cls.STATUS_OPTIONS,
                    'selected_options': cls._filter_options(
                        cls.STATUS_OPTIONS, status)
                }
            ]
        }

Tests

class TestFiltersAttachment(TestCase):
    def assert_attachment(self, attachment):
        self.assertEqual(attachment['fallback'], 'Filters')
        self.assertEqual(attachment['callback_id'], 'resource_filters')
        self.assertEqual(attachment['color'], '#d2dde1')
        self.assertEqual(attachment['mrkdwn_in'], ['text'])

        type_action = attachment['actions'][0]
        self.assertEqual(type_action['name'], 'resource_type')
        self.assertEqual(type_action['text'], 'Type')
        self.assertEqual(type_action['type'], 'select')
        self.assertEqual(type_action['options'][0]['text'], 'All types')
        self.assertEqual(type_action['options'][0]['value'], 'all')
        self.assertEqual(type_action['options'][1]['text'], ':link: Webpages')
        self.assertEqual(type_action['options'][1]['value'], 'web_pages')

        status_action = attachment['actions'][1]
        self.assertEqual(status_action['name'], 'resource_status')
        self.assertEqual(status_action['text'], 'Status')
        self.assertEqual(status_action['type'], 'select')
        self.assertEqual(status_action['options'][0]['text'], 'Available / Unavailable')
        self.assertEqual(status_action['options'][0]['value'], 'all')
        self.assertEqual(status_action['options'][1]['text'], ':white_circle: Available')
        self.assertEqual(status_action['options'][1]['value'], 'available')
        self.assertEqual(status_action['options'][2]['text'], ':red_circle: Unavailable')
        self.assertEqual(status_action['options'][2]['value'], 'unavailable')

    def test_all_type_selected(self):
        attachment = FiltersAttachment.build(check_type='all')
        self.assert_attachment(attachment)

        selected_type = attachment['actions'][0]['selected_options'][0]
        self.assertEqual(selected_type['text'], 'All types')
        self.assertEqual(selected_type['value'], 'all')

    def test_all_status_selected(self):
        attachment = FiltersAttachment.build(status='all')
        self.assert_attachment(attachment)

        selected_status = attachment['actions'][1]['selected_options'][0]
        self.assertEqual(selected_status['text'], 'Available / Unavailable')
        self.assertEqual(selected_status['value'], 'all')
        ...
ryche
  • 2,004
  • 2
  • 18
  • 27

1 Answers1

1

One of the criteria for the quality of a test suite is, how well the test suite supports you in case of test failures to identify the problem. Ideally, you should be able to identify the problem alone by looking at which tests failed and which did not. You should not need to use a debugger to find out what actually went wrong.

The way you have written your tests will not give you the best possible support. You have packed many assertions in one test function. Therefore, the test functions will fail for many different reasons, and when you see one of the functions fail, you will have to do a detailed analysis or use debugging to find out for which reason it failed. When making your tests check aspects redundantly (as you have asked in your question), you make them even less specific, which makes the problem worse.

Therefore, each test should check one specific aspect, such that a failure of a test gives the most specific information. This is achieved by the combination of the following two principles:

  • Each test should verify one specific aspect.
  • There should not be redundant tests for the same aspect.

Turning each assertion into a test of its own can be done conveniently with the help of so called parameterized tests. Some hints for Python can be found at this question: How do you generate dynamic (parameterized) unit tests in python?

Dirk Herrmann
  • 5,550
  • 1
  • 21
  • 47