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. 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

red: make it fail

I change test_failure

class TestDictionaries(unittest.TestCase):

    def test_make_a_dictionary(self):
        self.assertEqual(dict(), None)

the terminal shows AssertionError

AssertionError: {} != None

green: make it pass

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

self.assertEqual(dict(), {})

this is how to make an empty dictionary. I can make a dictionary with the dict constructor_ or curly braces({})

refactor: make it better

  • I add another assertion, this time with input

    self.assertEqual(dict(), {})
    self.assertEqual(dict(0), {})
    

    the terminal shows TypeError

    TypeError: 'int' object is not iterable
    
  • I add the error to the list of Exceptions_ encountered

    # Exceptions Encountered
    # AssertionError
    # TypeError
    
  • I change the value to an iterable_

    self.assertEqual(dict(), {})
    self.assertEqual(dict((0, 1)), {})
    

    the terminal shows TypeError

    TypeError: cannot convert dictionary update sequence element #0 to a sequence
    
  • I try a keyword argument instead

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

    the terminal shows AssertionError

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

    I change the expectation to match the values in the terminal

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

    the terminal shows green again. This test uses strings_ as keys


test_make_a_dictionary_w_none_as_a_key

red: make it fail

I add a test where I use None as a key in a dictionary

def test_make_a_dictionary_w_none_as_a_key(self):
    self.assertEqual({None: 'boom'}, {None: 'bap'})

the terminal shows AssertionError

AssertionError: {None: 'boom'} != {None: 'bap'}

green: make it pass

I change 'bap' to 'boom'

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

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


test_make_a_dictionary_w_a_boolean_as_a_key

red: make it fail

I add a test where I use a boolean_ as a key in a dictionary

def test_make_a_dictionary_w_none_as_a_key(self):
    ...

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

the terminal shows AssertionError

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

green: make it pass

I change 'bap' to 'boom'

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

the tests passes. I can use False as a key in a dictionary

refactor: make it better

I add True as a key

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

the terminal shows AssertionError

AssertionError: {False: 'boom', True: 'bap'} != {False: 'boom'}

I add the new key-value pair to the expectation

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

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


test_make_a_dictionary_w_a_number_as_a_key

red: make it fail

I add a failing test

def test_make_a_dictionary_w_a_boolean_as_a_key(self):
    ...

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

the terminal shows AssertionError

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

green: make it pass

I change 'bap' to 'boom'

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

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

refactor: make it better

I add a float_ as a key

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

the terminal shows AssertionError

AssertionError: {0: 'boom', 0.1: 'bap'} != {0: 'boom'}

I add the new key-value pair to the expectation

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

the test passes. I can use integers_ and floats_ as keys in a dictionary


test_make_a_dictionary_w_a_tuple_as_a_key

red: make it fail

I add a test for a tuple_ as a key

def test_make_a_dictionary_w_a_number_as_a_key(self):
    ...

def test_make_a_dictionary_w_a_tuple_as_a_key(self):
    self.assertEqual(
        {(0, 1): 'boom'},
        {(0, 1): 'bap'}
    )

the terminal shows AssertionError

AssertionError: {(0, 1): 'boom'} != {(0, 1): 'bap'}

green: make it pass

I change 'bap' to 'boom'

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

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


test_make_a_dictionary_w_a_list_as_a_key

red: make it fail

I add another test

def test_make_a_dictionary_w_a_tuple_as_a_key(self):
    ...

def test_make_a_dictionary_w_a_list_as_a_key(self):
    self.assertEqual(
        {[0, 1]: 'boom'}
    )

the terminal shows TypeError

TypeError: unhashable type: 'list'

only hashable_ objects_ can be used as keys in a dictionary

green: make it pass

I remove the things around the new dictionary then change the key for fun

def test_make_a_dictionary_w_a_list_as_a_key(self):
    {[3, 2, 1]: 'BOOM!'}

I add assertRaises_

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

the test passes. I cannot make a dictionary with a list as a key


test_make_a_dictionary_w_a_set_as_a_key

red: make it fail

I try the same thing with a set_ as a key

def test_make_a_dictionary_w_a_list_as_a_key(self):
    ...

def test_make_a_dictionary_w_a_set_as_a_key(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_a_set_as_a_key(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_a_dictionary_as_a_key

red: make it fail

I add a new test

def test_make_a_dictionary_w_a_set_as_a_key(self):
    ...

def test_make_a_dictionary_w_a_dictionary_as_a_key(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_a_dictionary_as_a_key(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

red: make it fail

I add a new test with the dir_ function to see the attributes and methods of dictionaries

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 move the terminal to right side of the screen so I can see the entire difference, then 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 and use find and replace to remove the characters that are not necessary

Note

your results can be different because of your Python version

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 and I move the terminal back to the bottom. I copy the names that do not have double underscores (__) to make a TODO list for the next set of tests

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

# Exceptions Encountered
# AssertionError
# TypeError

test_clear_empties_a_dictionary

red: make it fail

  • 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

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

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

    the terminal shows AssertionError

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

    the clear method emptied the dictionary

green: make it pass

I change the values to match

self.assertEqual(a_dictionary, {})

the test passes

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, {})
    
  • 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):
    ...

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

the terminal shows AssertionError

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

this method returns a copy of the dictionary

green: make it pass

I add the value to the assertion

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

the terminal shows AssertionError

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

I change the assert_ method

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'})
    
  • 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 to a tuple_

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

the terminal shows AssertionError

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

the fromkeys_ method returns a dictionary that uses the values in the iterable_ as keys with default values of None. I add the expected values

self.assertIsNone(
    a_dictionary.fromkeys((0, 1)),
    {0: None, 1: None}
)

the terminal shows AssertionError

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

I change the assertion

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

the test passes

refactor: make it better

  • I add an assert_ method 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 the assertion then change the call to use the dict class

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

    the test is still green. I remove a_dictionary since it is not used

    def test_fromkeys(self):
        self.assertEqual(
            dict.fromkeys((0, 1)),
            {0: None, 1: None}
        )
    
  • the dictionary the fromkeys_ method returns has None as a default value, I write it explicitly in the test

    self.assertEqual(
        dict.fromkeys((0, 1), None),
        {0: None, 1: None}
    )
    

    the terminal still shows green. I change it to see if I get a failure

    self.assertEqual(
        dict.fromkeys((0, 1), 'default'),
        {0: None, 1: None}
    )
    

    the terminal shows AssertionError

    AssertionError: {0: 'default', 1: 'default'} != {0: None, 1: None}
    

    I change the values to match

    self.assertEqual(
        dict.fromkeys((0, 1), 'default'),
        {0: 'default', 1: 'default'}
    )
    

    the test is green again

  • I rename the test

    def test_fromkeys_makes_a_dictionary_from_an_iterable(self):
        self.assertEqual(
            dict.fromkeys((0, 1), 'default'),
            {0: 'default', 1: 'default'}
        )
    
  • I remove fromkeys_ from the TODO list

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

test_get_a_value_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(0))

the terminal shows green

refactor: make it better

  • I add another assertion, this time with something from the dictionary

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

    the terminal shows AssertionError

    AssertionError: 'value' is not None
    

    it looks like the get_ method has a condition where it returns the value for the key it is given from the dictionary if it is there or returns None if the key is not there. I add the expected value

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

    the terminal shows AssertionError

    AssertionError: 'value' is not None : value
    

    I change the assertion

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

    the test passes

  • I change the key in the first assertion for fun

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

    the test is still green. I add a parameter to see if I can set a default value

    self.assertIsNone(a_dictionary.get('not_in_dictionary', None))
    

    the test is still passing. I change the value to see if I get an error

    self.assertIsNone(a_dictionary.get('not_in_dictionary', 'default'))
    

    the terminal shows AssertionError

    AssertionError: 'default' is not None
    

    I add the expectation

    self.assertIsNone(a_dictionary.get('not_in_dictionary', 'default'), 'default')
    

    the terminal shows AssertionError

    AssertionError: 'default' is not None : default
    

    I change the assertion

    self.assertEqual(a_dictionary.get('not_in_dictionary', 'default'), 'default')
    

    the test is green again

  • I do the same thing with the second assertion

    self.assertEqual(a_dictionary.get('key', 'default'), 'value')
    

    the test is still green. The get_ method returns the value for a given key in a dictionary or returns a default value if the key is not there

  • I change the name of the test

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

    the test is still green

  • I remove get_ from the TODO list

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

test_items_returns_key_value_pairs_of_a_dictionary

red: make it fail

I add a a test

def test_get_a_value_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

green: make it pass

I copy the value from the terminal and paste as the expectation

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

the terminal shows NameError_

NameError: name 'dict_items' is not defined

this new object contains a list, I will use it as the expectation instead

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

the terminal shows AssertionError

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

I pass the call to the items_ method to the list constructor_

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

the terminal shows AssertionError

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

I change the assertion

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

the test passes. It looks like the items_ method returns the key-value pairs of a dictionary as tuples_ in a list

refactor: make it better

  • I add another key-value pair to the dictionary

    def test_items(self):
        a_dictionary = {
            'key1': 'value1',
            'keyN': 'valueN',
        }
        self.assertEqual(list(a_dictionary.items()), [('key', 'value')])
    

    the terminal shows AssertionError

    AssertionError: Lists differ: [('key1', 'value1'), ('keyN', 'valueN')] != [('key', 'value')]
    

    I change the expectation to match

    self.assertEqual(
        list(a_dictionary.items()),
        [('key1', 'value1'), ('keyN', 'valueN')]
    )
    

    the test passes

  • I change the name of the test

    def test_items_returns_key_value_pairs_of_a_dictionary(self):
        a_dictionary = {
            'key1': 'value1',
            'keyN': 'valueN',
        }
        self.assertEqual(
            list(a_dictionary.items()),
            [('key1', 'value1'), ('keyN', 'valueN')]
        )
    
  • I remove items_ from the TODO list

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

test_keys_of_a_dictionary

red: make it fail

I add a a test

def test_items_returns_key_value_pairs_of_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 looks like the error in test_items_returns_key_value_pairs_of_a_dictionary

green: make it pass

I copy the value from the terminal and paste it as the expectation

self.assertIsNone(a_dictionary.keys(), dict_keys(['key']))

the terminal shows NameError_

NameError: name 'dict_keys' is not defined

the dict_keys object contains a list, I will use it as the expectation instead

self.assertIsNone(a_dictionary.keys(), ['key'])

the terminal shows AssertionError

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

I pass the call to the keys_ method to the list constructor_

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

the terminal shows AssertionError

AssertionError: ['key'] is not None : ['key']

I change assertIsNone_ to assertEqual_

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

the test passes

refactor: make it better

  • I add another key-value pair to the dictionary to see what the keys_ method returns when there are multiple

    def test_keys(self):
        a_dictionary = {
            'key1': 'value1',
            'keyN': 'valueN',
        }
        self.assertEqual(list(a_dictionary.keys()), ['key'])
    

    the terminal shows AssertionError

    AssertionError: Lists differ: ['key1', 'keyN'] != ['key']
    

    I change the expectation to match

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

    the test passes

  • I change the name of the test

    def test_keys_of_a_dictionary(self):
        a_dictionary = {
            'key1': 'value1',
            'keyN': 'valueN',
        }
        self.assertEqual(list(a_dictionary.keys()), ['key1', 'keyN'])
    
  • I remove keys_ from the TODO list

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

test_pop_removes_given_key_from_a_dictionary

red: make it fail

I wonder if the next method is the same as the one in test_pop_removes_and_returns_the_last_item_from_a_list, I add a test for it

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

this pop method is different from the one in lists

green: make it pass

  • I pass a value to the call

    self.assertIsNone(a_dictionary.pop(0))
    

    the terminal shows KeyError

    KeyError: 0
    
  • I add it to the list of Exceptions_ encountered

    # Exceptions Encountered
    # AssertionError
    # TypeError
    # KeyError
    
  • I remove the things around the call and change the value given to be more descriptive

    a_dictionary = {'key': 'value'}
    a_dictionary.pop('not in dictionary')
    

    the terminal shows KeyError

    KeyError: 'not in dictionary'
    

    I add assertRaises_

    a_dictionary = {'key': 'value'}
    
    with self.assertRaises(KeyError):
        a_dictionary.pop('not in dictionary')
    

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

refactor: make it better

  • I add another assertion

    a_dictionary = {'key': 'value'}
    self.assertIsNone(a_dictionary.pop('key'))
    
    with self.assertRaises(KeyError):
        a_dictionary.pop('not in dictionary')
    

    the terminal shows AssertionError

    AssertionError: 'value' is not None
    

    the pop method returns the value of the given key from the dictionary. I add the expectation

    self.assertIsNone(a_dictionary.pop('key'), 'value')
    

    the terminal shows AssertionError

    AssertionError: 'value' is not None : value
    

    I change the assertion to assertEqual_

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

    the test passes

  • I add another assertion to see what the method did 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 of the given key from the dictionary. I change the expectation to match

    self.assertEqual(a_dictionary, {})
    

    the test passes

  • I rename the test

    def test_pop_removes_given_key_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('not in dictionary')
    
  • 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_given_key_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 add the value from the terminal as an expectation

self.assertIsNone(a_dictionary.popitem(), ('key', 'value'))

the terminal shows AssertionError

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

I change the assertion

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

the test passes

refactor: make it better

  • I want to know what the popitem method did 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 from the dictionary

  • I change the value

    self.assertEqual(a_dictionary, {})
    

    the test passes

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

    a_dictionary = {
        'key1': 'value1',
        'keyN': 'valueN',
    }
    

    the terminal shows AssertionError

    AssertionError: Tuples differ: ('keyN', 'valueN') != ('key', 'value')
    

    I change the expectation to match

    self.assertEqual(a_dictionary.popitem(), ('keyN', 'valueN'))
    

    the terminal shows AssertionError

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

    I change the value to match

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

    the test passes

  • I add another call to the method

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

    the terminal shows AssertionError

    AssertionError: Tuples differ: ('key1', 'value1') != ('keyN', 'valueN')
    

    I change the expectation

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

    the test passes. popitem removes and returns the last key-value pair from a dictionary

  • 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',
            'keyN': 'valueN',
        }
        self.assertEqual(a_dictionary.popitem(), ('keyN', 'valueN'))
        self.assertEqual(a_dictionary, {'key1': 'value1'})
        self.assertEqual(a_dictionary.popitem(), ('key1', 'value1'))
    
  • 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 it did to 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 change the name of the value given

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

    the terminal shows AssertionError

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

    I change the expectation to match

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

    the test is green again

  • I add an assertion to see what happens when the key is already in the dictionary

    self.assertIsNone(a_dictionary.setdefault('new_key'))
    self.assertIsNone(a_dictionary.setdefault('key'))
    

    the terminal shows AssertionError

    AssertionError: 'value' is not None
    

    setdefault_ returns the value for a key in a dictionary when the key is in the dictionary. I add the value to the assertion

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

    the terminal shows AssertionError

    AssertionError: 'value' is not None : value
    

    I change the assertion to assertEqual_

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

    the test passes

  • It looks like setdefault_ has a condition where it sets a default value when the key is not in the dictionary and returns the value when the key is in it. I change the first assertion to find out

    self.assertIsNone(a_dictionary.setdefault('new_key', None))
    

    the terminal still shows green. I change the given default value expecting a failure

    self.assertIsNone(a_dictionary.setdefault('new_key', 'default'))
    

    the terminal shows AssertionError

    AssertionError: 'default' is not None
    

    I add the expected value

    self.assertIsNone(a_dictionary.setdefault('new_key', 'default'), 'default')
    

    the terminal shows AssertionError

    AssertionError: 'default' is not None : default
    

    I change the assert_ method

    self.assertEqual(a_dictionary.setdefault('new_key', 'default'), 'default')
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value', 'new_key': 'default'} != {'key': 'value', 'new_key': None}
    

    I change the expectation to match

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

    the test passes

  • I try the same thing with the second assertion

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

    the terminal still shows green. setdefault_ adds a given key to the dictionary with a given default value and returns the default value if the key is not in the dictionary. It returns the value for a key that is already in the dictionary

  • I rename the test

    def test_setdefault_adds_key_w_a_default_value_to_a_dictionary(self):
        a_dictionary = {'key': 'value'}
        self.assertEqual(a_dictionary.setdefault('new_key', 'default'), 'default')
        self.assertEqual(a_dictionary.setdefault('key', 'default'), 'value')
        self.assertEqual(
            a_dictionary,
            {
                'key': 'value',
                'new_key': 'default',
            }
        )
    
  • 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_setdefault_adds_key_w_a_default_value_to_a_dictionary(self):
        ...
    
    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 did to the dictionary

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

    the terminal shows AssertionError

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

    the dictionary stayed the same

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 add a value to the call to see what would happen

    self.assertIsNone(a_dictionary.update(0))
    

    the terminal shows TypeError

    TypeError: 'int' object is not iterable
    

    I change the value to a tuple_

    self.assertIsNone(a_dictionary.update((0, 1)))
    

    the terminal shows TypeError

    TypeError: cannot convert dictionary update sequence element #0 to a sequence
    

    I had this same error message in test_make_a_dictionary. I try a keyword argument

    self.assertIsNone(a_dictionary.update(new_key='new value'))
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value', 'new_key': 'new value'} != {'key': 'value'}
    

    I add the new key-value pair to the assertion

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

    the test passes

  • I add an assertion to see what would happen if I give a key that is already in the dictionary

    self.assertIsNone(a_dictionary.update(new_key='new value'))
    self.assertIsNone(a_dictionary.update(key='updated value'))
    

    the terminal shows AssertionError

    AssertionError: {'key': 'updated value', 'new_key': 'new value'} != {'key': 'value', 'new_key': 'new value'}
    

    the update_ method changes the value for a key that is already in a dictionary. I change the expectation to match. I change the expectation to match

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

    the test passes

  • since the update_ method takes keyword arguments it means I should be able to give it a dictionary as input. I add another assertion

    self.assertIsNone(a_dictionary.update({'another_key': 'another_value'}))
    

    the terminal shows AssertionError

    AssertionError: {'key': 'updated value', 'new_key': 'new value', 'another_key': 'another_value'} != {'key': 'updated value', 'new_key': 'new value'}
    

    the update_ method adds the key-value pairs from the given dictionary to the existing one. I change the expectation to match

    self.assertEqual(
        a_dictionary,
        {
            'key': 'updated value',
            'new_key': 'new value',
            '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(new_key='new value'))
        self.assertIsNone(a_dictionary.update(key='updated value'))
        self.assertIsNone(a_dictionary.update({'another_key': 'another_value'}))
        self.assertEqual(
            a_dictionary,
            {
                'key': 'updated value',
                'new_key': 'new value',
                '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_items_returns_key_value_pairs_of_a_dictionary and test_keys_of_a_dictionary

green: make it pass

I add the expected value

self.assertIsNone(a_dictionary.values, dict_values(['value']))

the terminal shows NameError_

NameError: name 'dict_values' is not defined

I use the list in the dict_values object_

self.assertIsNone(a_dictionary.values(), ['value'])

the terminal shows AssertionError

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

I change the assertIsNone_ to assertEqual_

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

the terminal shows AssertionError

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

I pass the call to the list constructor_

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

the test passes

refactor: make it better

  • I change the dictionary

    a_dictionary = {
        'key1': 'value1',
        'keyN': 'valueN',
    }
    

    the terminal shows AssertionError

    AssertionError: Lists differ: ['value1', 'valueN'] != ['value']
    

    I change the values in the expectation

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

    the test passes

  • I rename the test

    def test_values_of_a_dictionary(self):
        a_dictionary = {
            'key1': 'value1',
            'keyN': 'valueN',
        }
        self.assertEqual(
            list(a_dictionary.values()),
            ['value1', 'valueN']
        )
    
  • I remove values_ from the TODO list


test_key_error

The KeyError is an important Exception_ to know when working with a dictionary

red: make it fail

I add a test for getting the value of a key that is in a dictionary

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['not_in_dictionary'])
    

    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['not_in_dictionary']
    

    the test passes

  • I add an assertion to show that I can use the get_ method if I do not want to get KeyError with a key that is not in a dictionary

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

    the terminal shows AssertionError

    AssertionError: 'default' != 'value'
    

    I change the expectation

    self.assertEqual(a_dictionary.get('not_in_dictionary', 'default'), 'default')
    

    the test passes

  • Earlier on in test_pop_removes_given_key_from_a_dictionary the pop method raised KeyError with a key that was not in the dictionary, I add an assertion for it

    with self.assertRaises(KeyError):
        a_dictionary['not_in_dictionary']
    a_dictionary.pop('not_in_dictionary')
    

    the terminal shows KeyError

    KeyError: 'not_in_dictionary'
    

    I add assertRaises_

    with self.assertRaises(KeyError):
        a_dictionary['not_in_dictionary']
    with self.assertRaises(KeyError):
        a_dictionary.pop('not_in_dictionary')
    

    the test passes

  • The popitem method also raises KeyError when called on an empty dictionary

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

    the terminal shows KeyError

    KeyError: 'popitem(): dictionary is empty'
    

    I add assertRaises_

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

review

I ran tests for dictionaries

Would you like to test functions?


data structures: Dictionaries: tests