dictionaries


A dictionary also known as a Mapping contains key-value pairs, the values can be any Python object but not the keys.

I think this is the most important data structure to know as it can hold all the other data structures and in your programming journey you will come across JSON which you can read and write as dictionaries in Python

requirements

  • I open a terminal to run makePythonTdd.sh with dictionaries as the name of the project

    ./makePythonTdd.sh dictionaries
    

    on Windows without Windows Subsystem Linux use makePythonTdd.ps1

    ./makePythonTdd.ps1 dictionaries
    

    it makes the folders and files that are needed, installs packages, runs the first test, and the terminal shows AssertionError

    E       AssertionError: True is not false
    
    tests/test_dictionaries.py:7: AssertionError
    
  • I hold ctrl (windows/linux) or option (mac) on the keyboard and use the mouse to click on tests/test_dictionaries.py:7 to open it in the editor

  • then change True to False to make the test pass


test_make_a_dictionary_w_dict_constructor

red: make it fail

I change test_failure

class TestDictionaries(unittest.TestCase):

    def test_make_a_dictionary_w_dict_constructor(self):
        self.assertEqual(dict(key='value'), None)

the terminal shows AssertionError

AssertionError: {'key': 'value'} != None

green: make it pass

I copy the value from the terminal and paste it to replace None

self.assertEqual(dict(key='value'), {'key': 'value'})

the test passes

test_make_a_dictionary_w_curly_braces

I can make a dictionary with the dict constructor and the passing test shows I can make one with {}.

red: make it fail

I add a test

def test_make_a_dictionary_w_dict_constructor(self):
    ...

def test_make_a_dictionary_w_curly_braces(self):
    self.assertEqual({'key': 'value'}, dict(key='key'))

the terminal shows AssertionError

AssertionError: {'key': 'value'} != {'key': 'key'}

green: make it pass

I make the values match

self.assertEqual({'key': 'value'}, dict(key='value'))

the test


test_make_a_dictionary_w_booleans_as_keys

I wonder if it is possible to use False or True as dictionary keys

red: make it fail

I add a test

def test_make_a_dictionary_w_curly_braces(self):
    ...

def test_make_a_dictionary_w_booleans_as_keys(self):
    self.assertEqual({False: 'boom'}, {'False': 'boom'})

the terminal shows AssertionError

AssertionError: {False: 'boom'} != {'False': 'boom'}

green: make it pass

I match the keys

self.assertEqual({False: 'boom'}, {False: 'boom'})

I can use False as a key in a dictionary

refactor: make it better

I add another assertion

self.assertEqual({False: 'boom'}, {False: 'boom'})
self.assertEqual({True: 'bap'}, {'True': 'bap'})

the terminal shows AssertionError

AssertionError: {True: 'bap'} != {'True': 'bap'}

I change the expectation

self.assertEqual({True: 'bap'}, {True: 'bap'})

the test passes. I can use a boolean as a key in a dictionary


test_make_a_dictionary_w_numbers_as_keys

red: make it fail

I add a failing test

def test_make_a_dictionary_w_booleans_as_keys(self):
    ...

def test_make_a_dictionary_w_numbers_as_keys(self):
    self.assertEqual({0: 'boom'}, {'zero': 'boom'})

the terminal shows AssertionError

AssertionError: {0: 'boom'} != {'zero': 'boom'}

green: make it pass

I change the key in the exception

def test_make_a_dictionary_w_numbers_as_keys(self):
    self.assertEqual({0: 'boom'}, {0: 'boom'})

the test passes. I can use integers keys in a dictionary

refactor: make it better

I add an assertion for floats

self.assertEqual({0: 'boom'}, {0: 'boom'})
self.assertEqual({0.1: 'bap'}, {'0.1': 'bap'})

the terminal shows AssertionError

AssertionError: {0.1: 'bap'} != {'0.1': 'bap'}

I make the keys match

self.assertEqual({0.1: 'bap'}, {0.1: 'bap'})

the test passes. I can use integers and floats as keys in a dictionary


test_make_a_dictionary_w_tuples_as_keys

red: make it fail

I add a test for tuples

def test_make_a_dictionary_w_booleans_as_keys(self):
    ...

def test_make_a_dictionary_w_tuples_as_keys(self):
    self.assertEqual(
        {(0, 1): 'value'},
        {(0, 1): 'key'}
    )

the terminal shows AssertionError

AssertionError: {(0, 1): 'value'} != {(0, 1): 'key'}

green: make it pass

I make the values match

self.assertEqual(
    {(0, 1): 'value'},
    {(0, 1): 'value'}
)

the test passes


test_make_a_dictionary_w_lists_as_keys

red: make it fail

I add another test

def test_make_a_dictionary_w_tuples_as_keys(self):
    ...

def test_make_a_dictionary_w_lists_as_keys(self):
    self.assertEqual(
        {[3, 2, 1]: 'BOOM!'},
        {[3, 2, 1]: 'bap'}
    )

the terminal shows TypeError

TypeError: unhashable type: 'list'

only hashable objects can be used as dictionary keys and lists are not hashable

I add TypeError to the list of Exceptions encountered

# Exceptions Encountered
# AssertionError
# TypeError

green: make it pass

I change the assertEqual to assertRaises

def test_make_a_dictionary_w_lists_as_keys(self):
    with self.assertRaises(TypeError):
        {[3, 2, 1]: 'BOOM!'}

see how to test that an Exception is raised for more details on why that worked. I cannot make a dictionary with a list as a key


test_make_a_dictionary_w_sets_as_keys

red: make it fail

I try the same thing with a set as a key

def test_make_a_dictionary_w_sets_as_keys(self):
    {{3, 2, 1}: 'BOOM!'}

the terminal shows TypeError

TypeError: unhashable type: 'set'

green: make it pass

I add assertRaises

def test_make_a_dictionary_w_sets_as_keys(self):
    with self.assertRaises(TypeError):
        {{3, 2, 1}: 'BOOM!'}

the test is green again. I cannot use a set as a key in a dictionary


test_make_a_dictionary_w_dictionaries_as_keys

red: make it fail

I add a new test

def test_make_a_dictionary_w_sets_as_keys(self):
    ...

def test_make_a_dictionary_w_dictionaries_as_keys(self):
    a_dictionary = {'key': 'value'}
    {a_dictionary: 'BOOM!'}

the terminal shows TypeError

TypeError: unhashable type: 'dict'

green: make it pass

I add assertRaises

def test_make_a_dictionary_w_dictionaries_as_keys(self):
    a_dictionary = {'key': 'value'}
    with self.assertRaises(TypeError):
        {a_dictionary: 'BOOM!'}

the test passes. I cannot use a dictionary, set or list as a key in a dictionary they are not hashable


test_attributes_and_methods_of_dictionaries

I can use the dir function to look at the attributes and methods of dictionaries

red: make it fail

I add a new test

def test_attributes_and_methods_of_dictionaries(self):
    self.assertEqual(
        dir(dict),
        []
    )

the terminal shows AssertionError

AssertionError: Lists differ: ['__class__', '__class_getitem__', '__cont[530 chars]ues'] != []

It also gives me a message to view the entire difference between the two lists

Diff is 720 characters long. Set self.maxDiff to None to see it.

green: make it pass

I add unittest.TestCase.maxDiff

def test_attributes_and_methods_of_dictionaries(self):
    self.maxDiff = None
    self.assertEqual(
        dir(dict),
        []
    )

the terminal shows the entire difference between the two lists. I copy and paste the expected values from the terminal

Note

Your results can be different because of your version of Python

def test_attributes_and_methods_of_dictionaries(self):
    self.maxDiff = None
    self.assertEqual(
        dir(src.dictionaries.a_dict()),
        [
              '__class__',
              '__class_getitem__',
              '__contains__',
              '__delattr__',
              '__delitem__',
              '__dir__',
              '__doc__',
              '__eq__',
              '__format__',
              '__ge__',
              '__getattribute__',
              '__getitem__',
              '__getstate__',
              '__gt__',
              '__hash__',
              '__init__',
              '__init_subclass__',
              '__ior__',
              '__iter__',
              '__le__',
              '__len__',
              '__lt__',
              '__ne__',
              '__new__',
              '__or__',
              '__reduce__',
              '__reduce_ex__',
              '__repr__',
              '__reversed__',
              '__ror__',
              '__setattr__',
              '__setitem__',
              '__sizeof__',
              '__str__',
              '__subclasshook__',
              'clear',
              'copy',
              'fromkeys',
              'get',
              'items',
              'keys',
              'pop',
              'popitem',
              'setdefault',
              'update',
              'values'
        ]
    )

the test passes. I make a TODO list with the names that do not have double underscores (__)

'clear',
'copy',
'fromkeys',
'get',
'items',
'keys',
'pop',
'popitem',
'setdefault',
'update',
'values'

# Exceptions Encountered
# AssertionError
# TypeError

test_clear_empties_a_dictionary

I add a test for the method

def test_attributes_and_methods_of_dictionaries(self):
    ...

def test_clear(self):
    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.clear())

the terminal shows green. The clear method returns None

red: make it fail

I add an assertion to see what changed in the dictionary

self.assertIsNone(a_dictionary.clear())
self.assertEqual(a_dictionary, {'key': 'value'})

the terminal shows AssertionError

AssertionError: {} != {'key': 'value'}

the clear method empties the dictionary

green: make it pass

I change the values to match

self.assertEqual(a_dictionary, {})

the test passes. {} is how Python represents an empty dictionary

refactor: make it better

  • I rename the test

    def test_clear_empties_a_dictionary(self):
        a_dictionary = {'key': 'value'}
        self.assertIsNone(a_dictionary.clear())
        self.assertEqual(a_dictionary, {})
    

    the test is still passing

  • I remove clear from the TODO list

    'copy',
    'fromkeys',
    'get',
    'items',
    'keys',
    'pop',
    'popitem',
    'setdefault',
    'update',
    'values'
    

test_copy_a_dictionary

red: make it fail

I add a test for the next method

def test_clear_empties_a_dictionary(self):
    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.clear())
    self.assertEqual(a_dictionary, {})

def test_copy(self):
    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.copy())

the terminal shows AssertionError

AssertionError: {'key': 'value'} is not None

the copy method returns a copy of the dictionary

green: make it pass

I change the assert method then add the expected value

def test_copy(self):
    a_dictionary = {'key': 'value'}
    self.assertEqual(a_dictionary.copy(), {'key': 'value'})

the test passes

refactor: make it better

  • I add another assertion to see what happens to the dictionary after the call

    self.assertEqual(a_dictionary.copy(), {'key': 'value'})
    self.assertEqual(a_dictionary, {})
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value'} != {}
    

    it stays the same. I change the values to match

    self.assertEqual(a_dictionary, {'key': 'value'})
    

    the test is green again

  • I rename the test

    def test_copy_a_dictionary(self):
        a_dictionary = {'key': 'value'}
        self.assertEqual(a_dictionary.copy(), {'key': 'value'})
        self.assertEqual(a_dictionary, {'key': 'value'})
    

    the test is still green

  • I remove copy from the TODO list

    'fromkeys',
    'get',
    'items',
    'keys',
    'pop',
    'popitem',
    'setdefault',
    'update',
    'values'
    

test_fromkeys_makes_a_dictionary_from_an_iterable

red: make it fail

I add a test

def test_copy_a_dictionary(self):
    ...

def test_fromkeys(self):
    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.fromkeys())

the terminal shows TypeError

TypeError: fromkeys expected at least 1 argument, got 0

green: make it pass

I pass a value to the call

self.assertIsNone(a_dictionary.fromkeys(0))

the terminal shows TypeError

TypeError: 'int' object is not iterable

I change the value passed in to a tuple

self.assertIsNone(a_dictionary.fromkeys((0, 1, 2, 3)))

the terminal shows AssertionError

AssertionError: {0: None, 1: None, 2: None, 3: None} is not None

the fromkeys <https://docs.python.org/3/library/stdtypes.html#dict.fromkeys> method returns a dictionary that uses the iterable given as keys with default values of None. I change the assertion then add the expected values

def test_fromkeys(self):
    a_dictionary = {'key': 'value'}
    self.assertEqual(
        a_dictionary.fromkeys((0, 1, 2, 3)),
        {0: None, 1: None, 2: None, 3: None}
    )

the test passes

refactor: make it better

  • I add an assert statement to see what happens to the first dictionary in the test

    self.assertEqual(
        a_dictionary.fromkeys((0, 1, 2, 3)),
        {0: None, 1: None, 2: None, 3: None}
    )
    self.assertEqual(a_dictionary, {})
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value'} != {}
    

    the dictionary did not change

  • I remove it and the assertion

    def test_fromkeys(self):
        self.assertEqual(
            a_dictionary.fromkeys((0, 1, 2, 3)),
            {0: None, 1: None, 2: None, 3: None}
        )
    

    the terminal shows NameError

    NameError: name 'a_dictionary' is not defined
    

    I change the call to use the dict constructor

    def test_fromkeys(self):
        self.assertEqual(
            dict.fromkeys((0, 1, 2, 3)),
            {0: None, 1: None, 2: None, 3: None}
        )
    

    the test passes

  • I rename the test

    def test_fromkeys_makes_a_dictionary_from_an_iterable(self):
        self.assertEqual(
            dict.fromkeys((0, 1, 2, 3)),
            {0: None, 1: None, 2: None, 3: None}
        )
    

    still green

  • I remove fromkeys from the TODO list

    'get',
    'items',
    'keys',
    'pop',
    'popitem',
    'setdefault',
    'update',
    'values'
    

test_get_value_of_key_from_a_dictionary

red: make it fail

I add another test

def test_fromkeys_makes_a_dictionary_from_an_iterable(self):
    ...

def test_get(self):
    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.get())

the terminal shows TypeError

TypeError: get expected at least 1 argument, got 0

green: make it pass

  • I add a value to the call

    self.assertIsNone(a_dictionary.get('key'))
    

    the terminal shows AssertionError

    AssertionError: 'value' is not None
    

    the get method returns the value for the key it is given from the dictionary

  • I change the assertion then add the expected value

    def test_get(self):
        a_dictionary = {'key': 'value'}
        self.assertEqual(a_dictionary.get('key'), 'value')
    

    the test passes

refactor: make it better

  • I add an assertion to see if the dictionary changed

    self.assertEqual(a_dictionary.get('key'), 'value')
    self.assertEqual(a_dictionary, {})
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value'} != {}
    

    it stayed the same

  • I remove the new statement, I want to see what happens when I give the get method a key that is not in the dictionary

    self.assertEqual(a_dictionary.get('key'), 'value')
    self.assertEqual(a_dictionary.get(0), {})
    

    the terminal shows AssertionError

    AssertionError: None != {}
    

    the get method returns None when the given key is not in the dictionary. I remove the expectation and change the assertion

    self.assertIsNone(a_dictionary.get(0))
    

    the test passes

  • I change the name of the test

    def test_get_value_of_key_from_a_dictionary(self):
        a_dictionary = {'key': 'value'}
        self.assertEqual(a_dictionary.get('key'), 'value')
        self.assertIsNone(a_dictionary.get(0))
    

    the test is still green

  • I remove get from the TODO list

    'items',
    'keys',
    'pop',
    'popitem',
    'setdefault',
    'update',
    'values'
    

test_pop_removes_key_and_returns_its_value_from_a_dictionary

red: make it fail

I add a test for the next method

def test_get_value_of_key_from_a_dictionary(self):
    ...

def test_items(self):
    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.items())

the terminal shows AssertionError

AssertionError: dict_items([('key', 'value')]) is not None

the items methods returns a dict_items object that contains the keys and values of the dictionary

green: make it pass

I copy and paste the value from the terminal

self.assertIsNone(a_dictionary.items(), dict_items([('key', 'value')]))

the terminal shows AssertionError

NameError: name 'dict_items' is not defined

the dict_items object has a list that contains a tuple, I use that

self.assertIsNone(a_dictionary.items(), [('key', 'value')])

the terminal shows AssertionError

AssertionError: dict_items([('key', 'value')]) is not None : [('key', 'value')]

I change the assertion and wrap the call in the list constructor

self.assertEqual(list(a_dictionary.items()), [('key', 'value')])

the test passes

refactor: make it better

  • I rename the test

    def test_pop_removes_key_and_returns_its_value_from_a_dictionary(self):
        a_dictionary = {'key': 'value'}
        self.assertEqual(list(a_dictionary.items()), [('key', 'value')])
    

    the test is still green

  • I remove items from the TODO list

    'keys',
    'pop',
    'popitem',
    'setdefault',
    'update',
    'values'
    

test_keys_of_a_dictionary

red: make it fail

I add the next test

def test_pop_removes_key_and_returns_its_value_from_a_dictionary(self):
    ...

def test_keys(self):
    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.keys())

the terminal shows AssertionError

AssertionError: dict_keys(['key']) is not None

this is like test_pop_removes_key_and_returns_its_value_from_a_dictionary

green: make it pass

I copy and paste the values from the terminal then change the assertion

def test_keys(self):
    a_dictionary = {'key': 'value'}
    self.assertEqual(a_dictionary.keys(), dict_keys(['key']))

the terminal shows NameError

NameError: name 'dict_keys' is not defined

the keys method returns dict_keys object that has the keys of the dictionary. I change the expectation to a list and use the list constructor to wrap the call

self.assertEqual(list(a_dictionary.keys()), ['key'])

the test passes

refactor: make it better

  • I rename the test

    def test_keys_of_a_dictionary(self):
        a_dictionary = {'key': 'value'}
        self.assertEqual(list(a_dictionary.keys()), ['key'])
    

    the test is still green

  • I remove keys from the TODO list

    'pop',
    'popitem',
    'setdefault',
    'update',
    'values'
    

test_pop_removes_and_returns_key_w_value_from_a_dictionary

red: make it fail

I add a test for the next method

def test_keys_of_a_dictionary(self):
    ...

def test_pop(self):
    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.pop())

the terminal shows TypeError

TypeError: pop expected at least 1 argument, got 0

green: make it pass

I pass a value to the call

def test_pop(self):
    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.pop(0))

the terminal shows KeyError

KeyError: 0

I change the assertion to assertRaises

def test_pop(self):
    a_dictionary = {'key': 'value'}

    with self.assertRaises(KeyError):
        a_dictionary.pop(0)

calling the pop method with a key that is not in the dictionary raises a KeyError

refactor: make it better

  • I add another assertion

    def test_pop(self):
        a_dictionary = {'key': 'value'}
        self.assertIsNone(a_dictionary.pop('key'))
    
        with self.assertRaises(KeyError):
            a_dictionary.pop(0)
    

    the terminal shows AssertionError

    AssertionError: 'value' is not None
    

    the pop method returns the value from the dictionary for the key it is given

  • I change the assertion to assertEqual and paste the value from the terminal

    a_dictionary = {'key': 'value'}
    self.assertEqual(a_dictionary.pop('key'), 'value')
    

    the test passes

  • I add another assertion to see what happens to the dictionary

    self.assertEqual(a_dictionary.pop('key'), 'value')
    self.assertEqual(a_dictionary, {'key': 'value'})
    

    the terminal shows AssertionError

    AssertionError: {} != {'key': 'value'}
    

    pop method removes the key-value pair and returns the value from the dictionary for the key it is given

  • I change the value expectation to match

    self.assertEqual(a_dictionary, {})
    

    the test passes

  • I rename the test

    def test_pop_removes_and_returns_key_w_value_from_a_dictionary(self):
        a_dictionary = {'key': 'value'}
        self.assertEqual(a_dictionary.pop('key'), 'value')
        self.assertEqual(a_dictionary, {})
    
        with self.assertRaises(KeyError):
            a_dictionary.pop(0)
    

    the test is still passings

  • I remove pop from the TODO list

    'popitem',
    'setdefault',
    'update',
    'values'
    

test_popitem_removes_and_returns_last_key_value_pair_from_a_dictionary

red: make it fail

I add a failing test

def test_pop_removes_and_returns_key_w_value_from_a_dictionary(self):
    ...

def test_pop_item(self):
    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.popitem())

the terminal shows AssertionError

AssertionError: ('key', 'value') is not None

the popitem method returns the key-value pair as a tuple

green: make it pass

I change the assertion and paste the value from the terminal

a_dictionary = {'key': 'value'}
self.assertEqual(a_dictionary.popitem(), ('key', 'value'))

the test passes

refactor: make it better

  • I want to know what the popitem method does to the dictionary

    self.assertEqual(a_dictionary.popitem(), ('key', 'value'))
    self.assertEqual(a_dictionary, {'key': 'value'})
    

    the terminal shows AssertionError

    AssertionError: {} != {'key': 'value'}
    

    popitem removes and returns the key-value pair given from the dictionary

  • I change the value

    self.assertEqual(a_dictionary, {})
    

    the test passes

  • I add another assertion

    self.assertEqual(a_dictionary, {})
    self.assertEqual(a_dictionary.popitem(), None)
    

    the terminal shows KeyError

    KeyError: 'popitem(): dictionary is empty'
    

    I change the assertion to assertRaises

    self.assertEqual(a_dictionary, {})
    
    with self.assertRaises(KeyError):
        a_dictionary.popitem()
    

    the test passes

  • this operation does not take input, I change the dictionary to see how it behaves

    a_dictionary = {
        'key1': 'value1',
        'key2': 'value2'
    }
    

    the terminal shows AssertionError

    AssertionError: Tuples differ: ('key2', 'value2') != ('key', 'value')
    

    I change the expectation to match

    self.assertEqual(
        a_dictionary.popitem(),
        ('key2', 'value2')
    )
    

    the terminal shows AssertionError

    AssertionError: {'key1': 'value1'} != {}
    

    I change the value to match

    self.assertEqual(a_dictionary, {'key1': 'value1'})
    

    the terminal shows AssertionError

    AssertionError: KeyError not raised
    

    I change the assertRaises to assertEqual

    self.assertEqual(
        a_dictionary.popitem(),
        ('key1', 'value1')
    )
    

    popitem returns the last key-value pair in the dictionary

  • I add another call to the method that should fail

    self.assertEqual(
        a_dictionary.popitem(),
        ('key1', 'value1')
    )
    a_dictionary.popitem()
    

    the terminal shows KeyError

    KeyError: 'popitem(): dictionary is empty'
    

    I add assertRaises

    self.assertEqual(
        a_dictionary.popitem(),
        ('key1', 'value1')
    )
    
    with self.assertRaises(KeyError):
        a_dictionary.popitem()
    

    the test passes

  • I change the name of the test

    def test_popitem_removes_and_returns_last_key_value_pair_from_a_dictionary(self):
        a_dictionary = {
            'key1': 'value1',
            'key2': 'value2'
        }
        self.assertEqual(
            a_dictionary.popitem(),
            ('key2', 'value2')
        )
        self.assertEqual(a_dictionary, {'key1': 'value1'})
        self.assertEqual(
            a_dictionary.popitem(),
            ('key1', 'value1')
        )
    
        with self.assertRaises(KeyError):
            a_dictionary.popitem()
    
  • I remove popitem from the TODO list

    'setdefault',
    'update',
    'values'
    

test_setdefault_adds_key_w_a_default_value_to_a_dictionary

red: make it fail

I add a test

def test_setdefault(self):
    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.setdefault())

the terminal shows TypeError

TypeError: setdefault expected at least 1 argument, got 0

green: make it pass

I pass a value in the call

self.assertIsNone(a_dictionary.setdefault(0))

the test passes

refactor: make it better

  • I add an assertion to see what changed in the dictionary

    self.assertIsNone(a_dictionary.setdefault(0))
    self.assertEqual(a_dictionary, {'key': 'value'})
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value', 0: None} != {'key': 'value'}
    

    setdefault adds the given key to the dictionary with a default value of None and returns the default value

  • I change the expectation to match

    self.assertEqual(
        a_dictionary,
        {
            'key': 'value',
            0: None,
        }
    )
    

    the test passes

  • I add another test to see what happens when the key is already in the dictionary

    self.assertEqual(
        a_dictionary,
        {
            'key': 'value',
            0: None,
        }
    )
    self.assertIsNone(a_dictionary.setdefault('key'))
    

    the terminal shows AssertionError

    AssertionError: 'value' is not None
    

    setdefault returns the value for the key in a dictionary when the key already exists

  • I paste the value from the terminal then change the assertion to assertEqual

    self.assertEqual(
        a_dictionary.setdefault('key'), 'value'
    )
    

    the test passes

  • I rename the test

    def test_setdefault_adds_key_w_a_default_value_to_a_dictionary(self):
        a_dictionary = {'key': 'value'}
        self.assertIsNone(a_dictionary.setdefault(0))
        self.assertEqual(
            a_dictionary,
            {
                'key': 'value',
                0: None,
            }
        )
        self.assertEqual(
            a_dictionary.setdefault('key'), 'value'
        )
    

    the test is still green

  • I remove setdefault from the TODO list

    'update',
    'values'
    

test_update_a_dictionary

red: make it fail

  • I add a test for the next method

    def test_update(self):
        a_dictionary = {'key': 'value'}
        self.assertIsNone(a_dictionary.update())
    

    the test is green. The update method returns None

  • I add an assertion to see what it does to the dictionary

        self.assertIsNone(a_dictionary.update())
    
    self.assertEqual(a_dictionary, {})
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value'} != {}
    

    the dictionary stayed the same, the test has to get better

green: make it pass

I change the values in the expectation to match the terminal

self.assertEqual(a_dictionary, {'key': 'value'})

the test passes

refactor: make it better

  • I check the Python documentation for the update method and see that it takes a dictionary as input. I add one to the call

    def test_update(self):
        a_dictionary = {'key': 'value'}
        self.assertIsNone(a_dictionary.update({'key1': 'value1'}))
        self.assertEqual(a_dictionary, {'key': 'value'})
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value', 'key1': 'value1'} != {'key': 'value'}
    

    the update method adds the key-value pairs from the given dictionary to the existing one

  • I change the expectation to match the values from the terminal

    self.assertIsNone(a_dictionary.update({'key1': 'value1'}))
    self.assertEqual(
        a_dictionary,
        {
            'key': 'value',
            'key1': 'value1'
        }
    )
    

    the test passes

  • I add another assertion

    self.assertIsNone(a_dictionary.update({'key1': 'value1'}))
    self.assertIsNone(a_dictionary.update(another_key='another value'))
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value', 'key1': 'value1', 'another_key': 'another value'} != {'key': 'value', 'key1': 'value1'}
    

    the update method accepts keyword arguments. I change the values to match

    self.assertEqual(
        a_dictionary,
        {
            'key': 'value',
            'key1': 'value1',
            'another_key': 'another value'
        }
    )
    

    the test passes

  • I want to know what would happen if the key already exists in the dictionary

    self.assertIsNone(a_dictionary.update(another_key='another value'))
    self.assertIsNone(a_dictionary.update(key='new value'))
    

    the terminal shows AssertionError

    AssertionError: {'key': 'new value', 'key1': 'value1', 'another_key': 'another value'} != {'key': 'value', 'key1': 'value1', 'another_key': 'another value'}
    

    the update method changes the value for an existing key in a dictionary. I change the expectation to match

    self.assertEqual(
        a_dictionary,
        {
            'key': 'new value',
            'key1': 'value1',
            'another_key': 'another value'
        }
    )
    

    the test passes

  • I rename the test

    def test_update_a_dictionary(self):
        a_dictionary = {'key': 'value'}
        self.assertIsNone(a_dictionary.update({'key1': 'value1'}))
        self.assertIsNone(a_dictionary.update(another_key='another value'))
        self.assertIsNone(a_dictionary.update(key='new value'))
        self.assertEqual(
            a_dictionary,
            {
                'key': 'new value',
                'key1': 'value1',
                'another_key': 'another value'
            }
        )
    

    the test is still green

  • I remove update from the TODO list

    'values'
    

test_values_of_a_dictionary

red: make it fail

I add a test for the last method

def test_update_a_dictionary(self):
      ...

def test_values(self):
    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.values())

the terminal shows AssertionError

AssertionError: dict_values(['value']) is not None

this is like test_pop_removes_key_and_returns_its_value_from_a_dictionary and test_keys_of_a_dictionary

green: make it pass

I change the assertIsNone to assertEqual and change the expectation to a list

a_dictionary = {'key': 'value'}
self.assertEqual(a_dictionary.values(), ['value'])

the terminal shows AssertionError

AssertionError: dict_values(['value']) != ['value']

I wrap the call in the list constructor

self.assertEqual(list(a_dictionary.values()), ['value'])

the test passes

refactor: make it better

  • I add more keys and values to the dictionary

    def test_values(self):
        a_dictionary = {
            'a_key': 'a value',
            'another_key': 'another value',
        }
    

    the terminal shows AssertionError

    AssertionError: Lists differ: ['a value', 'another value'] != ['value']
    

    I change the values

    self.assertEqual(
        list(a_dictionary.values()),
        ['a value', 'another value']
    )
    

    the test passes

  • I rename the test

    def test_values_of_a_dictionary(self):
        a_dictionary = {
            'a_key': 'a value',
            'another_key': 'another value',
        }
        self.assertEqual(
            list(a_dictionary.values()),
            ['a value', 'another value']
        )
    

    the test passes

  • I remove values from the TODO list


test_key_error

red: make it fail

I add a test

def test_values_of_a_dictionary(self):
    ...

def test_key_error(self):
    a_dictionary = {'key': 'value'}
    self.assertEqual(a_dictionary['key'], '')

the terminal shows AssertionError

AssertionError: 'value' != ''

I can get the value for a key in a dictionary by giving it in [], this is like viewing items in a list

green: make it pass

I change the value in the expectation to match the terminal

self.assertEqual(a_dictionary['key'], 'value')

the test passes

refactor: make it better

  • I add another assertion

    self.assertEqual(a_dictionary['key'], 'value')
    self.assertEqual(a_dictionary['key_not_in_dictionary'], 'value')
    

    the terminal shows KeyError

    KeyError: 'key_not_in_dictionary'
    

    I change the assertEqual to assertRaises

    self.assertEqual(a_dictionary['key'], 'value')
    
    with self.assertRaises(KeyError):
        a_dictionary['key_not_in_dictionary']
    

    the test passes

  • I know from test_popitem_removes_and_returns_last_key_value_pair_from_a_dictionary that I get KeyError when I call the method on an empty dictionary

    with self.assertRaises(KeyError):
        a_dictionary['key_not_in_dictionary']
    {}.popitem()
    

    the terminal shows KeyError

    KeyError: 'popitem(): dictionary is empty'
    

    I add assertRaises

    with self.assertRaises(KeyError):
        a_dictionary['key_not_in_dictionary']
    with self.assertRaises(KeyError):
        {}.popitem()
    
  • I also get KeyError when I call pop with a key that does not exist in the dictionary

    with self.assertRaises(KeyError):
        {}.popitem()
    a_dictionary.pop('key_not_in_dictionary')
    

    the terminal shows KeyError

    KeyError: 'key_not_in_dictionary'
    

    I add assertRaises

    with self.assertRaises(KeyError):
        {}.popitem()
    with self.assertRaises(KeyError):
        a_dictionary.pop('key_not_in_dictionary')
    
  • I can use the get method to avoid the KeyError when the key does not exist in a dictionary

    with self.assertRaises(KeyError):
        a_dictionary.pop('key_not_in_dictionary')
    
    
    self.assertEqual(
        a_dictionary.get('key_not_in_dictionary'),
        'value'
    )
    

    the terminal shows AssertionError

    AssertionError: None != 'value'
    

    I remove the expected value and change assertEqual to assertIsNone

    with self.assertRaises(KeyError):
        a_dictionary.pop('key_not_in_dictionary')
    
    self.assertIsNone(a_dictionary.get('key_not_in_dictionary'))
    

    the test is green again


review

I ran tests for dictionaries

Would you like to test functions?


data structures: Dictionaries: tests