Danger

DANGER WILL ROBINSON! Though the code works, this chapter is still UNDER CONSTRUCTION it may look completely different when I am done

dictionaries


A dictionary also known as a Mapping contains key-value pairs, the values can be any Python object. I will add tests for the keys to see which data types I can use.

I think this is the most important data structure to know because they can hold all the other data structures. In programming I have had to work with JSON which I 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 I change True to False to make the test pass

    7self.assertFalse(False)
    
  • I change the name of the class to match the CapWords format

    4class TestDictionaries(unittest.TestCase):
    

test_making_a_dictionary

red: make it fail

I change test_failure to test_making_a_dictionary

1import unittest
2
3
4class TestDictionaries(unittest.TestCase):
5
6    def test_making_a_dictionary(self):
7        self.assertEqual(dict(), None)

the terminal shows AssertionError

AssertionError: {} != None

green: make it pass

I change the expectation to match

self.assertEqual(dict(), {})

the test passes. These are two ways to make an empty dictionary

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 in test_dictionaries.py

    # Exceptions Encountered
    # AssertionError
    # TypeError
    
  • I change the value to a tuple since it is 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

    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. I can make a dictionary with the dict constructor or curly braces({}) and I used a string as a key in this test


test_making_a_dictionary_w_none_as_a_key

red: make it fail

I add a test to see if I can use None as a key in a dictionary

def test_making_a_dictionary(self):
    ...

def test_making_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 and strings as keys in a dictionary


test_making_a_dictionary_w_a_boolean_as_a_key

red: make it fail

I add a test to see if I can use a boolean as a key in a dictionary

def test_making_a_dictionary_w_none_as_a_key(self):
    ...

def test_making_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 to the assertion

def test_making_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 booleans, None and strings as keys in a dictionary


test_making_a_dictionary_w_a_number_as_a_key

red: make it fail

I add a failing test to see if I can use a number as a key in a dictionary

def test_making_a_dictionary_w_a_boolean_as_a_key(self):
    ...

def test_making_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 want to see if I can use a float as a key in a dictionary

def test_making_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 numbers, booleans, None and strings as keys in a dictionary


test_making_a_dictionary_w_a_tuple_as_a_key

red: make it fail

I add a test to see if I can use a tuple as a key in a dictionary

def test_making_a_dictionary_w_a_number_as_a_key(self):
    ...

def test_making_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 tuples, numbers (floats and integers), booleans, None and strings as keys in a dictionary


test_making_a_dictionary_w_a_list_as_a_key

red: make it fail

I add a test for lists

def test_making_a_dictionary_w_a_tuple_as_a_key(self):
    ...

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

the terminal shows TypeError

TypeError: unhashable type: 'list'

green: make it pass

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

def test_making_a_dictionary_w_a_list_as_a_key(self):

    {[3, 2, 1]: 'BOOM!!!'}

the terminal still shows TypeError, I add assertRaises

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

the test passes. I cannot use a list as a key in a dictionary


test_making_a_dictionary_w_a_set_as_a_key

red: make it fail

I try another test with a set as a key in a dictionary

def test_making_a_dictionary_w_a_list_as_a_key(self):
    ...

def test_making_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_making_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 lists or sets as keys in a dictionary


test_making_a_dictionary_w_a_dictionary_as_a_key

red: make it fail

I add another test

def test_making_a_dictionary_w_a_set_as_a_key(self):
    ...

def test_making_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_making_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 dictionaries, sets or lists as keys in a dictionary they are not hashable, which means they can change


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 show the full 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, then add maxDiff to the test

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

the terminal shows the difference between the two lists. I copy and paste the expected values from the terminal then use find and replace to remove the extra characters

Note

results can be different because of the Python version

def test_attributes_and_methods_of_dictionaries(self):
    self.maxDiff = None
    self.assertEqual(
        dir(src.dictionaries.a_dict()),
        [
            '__class__',
            ...
            '__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 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 first 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, same as it does with lists

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 assertIsNone to assertEqual

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. This method also works the same way with lists

  • 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 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 assertIsNone to assertEqual

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

the test passes

refactor: make it better

  • I add another assert method to see what happens to the first dictionary in the test

    self.assertEqual(
        a_dictionary.fromkeys((0, 1)),
        {0: None, 1: 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 made with the fromkeys method has None as the default values. When I called the method without inputs the terminal showed TypeError

    TypeError: fromkeys expected at least 1 argument, got 0
    

    I add a second input to see what will happen

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

    the terminal still shows green. I change the second input expecting 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. This is like a dict comprehension because it made a dictionary using the items from the iterable as keys

  • 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_value_of_a_key_in_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

  • this method also expected at least 1 argument, I add None to the call

    self.assertIsNone(a_dictionary.get(0, None))
    

    the terminal still shows green. I change the second argument expecting a failure

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

    the terminal shows AssertionError

    AssertionError: 'default' is not None
    

    I add the expectation

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

    the terminal shows AssertionError

    AssertionError: 'default' is not None : default
    

    I change the assertion

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

    the test passes. I change the 0 to be more descriptive

    self.assertEqual(
        a_dictionary.get('not_in_dictionary', 'default'),
        'default'
    )
    
  • I want to see what would happen if I use the get method with a key that is in the dictionary

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

    the terminal shows AssertionError

    AssertionError: 'value' != 'default'
    

    the get method has a condition. When the key is NOT in the dictionary, it returns the default argument, when the key is in the dictionary, it returns its value. I change the expectation to match

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

    the test passes

  • I change the name of the test

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

    the test is still green

  • I remove get from the TODO list

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

test_items_returns_iterable_of_key_value_pairs_of_a_dictionary

red: make it fail

I add the next test from the list

def test_get_value_of_a_key_in_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 it 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 and I know how to work with lists, I remove the stuff around it

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 to see what happens

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

the terminal shows AssertionError

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

the values are the same, I change assertIsNone to assertEqual

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

the test passes. This works because the items method returns an iterable of the key-value pairs of the dictionary

refactor: make it better

  • I add another key-value pair to the dictionary to see what the method does when there is more than one key-value pair

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

    the terminal shows AssertionError

    AssertionError: Lists differ: [('key1', 'value1'), ('keyN', [0, 1, 2, 'n'])] != [('key', 'value')]
    

    I change the expectation to match

    self.assertEqual(
        list(a_dictionary.items()),
        [
            ('key1', 'value1'),
            ('keyN', [0, 1, 2, 'n']),
        ]
    )
    

    the test passes

  • I change the name of the test

    def test_items_returns_iterable_of_key_value_pairs_of_a_dictionary(self):
        a_dictionary = {
            'key1': 'value1',
            'keyN': [0, 1, 2, 'n'],
        }
        self.assertEqual(
            list(a_dictionary.items()),
            [
                ('key1', 'value1'),
                ('keyN', [0, 1, 2, 'n']),
            ]
        )
    
  • I remove items from the TODO list

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

    all tests are still passing


test_keys_of_a_dictionary

red: make it fail

I add a test

def test_items_returns_iterable_of_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_iterable_of_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': [0, 1, 2, 'n'],
        }
        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': [0, 1, 2, 'n'],
        }
        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_and_returns_its_value

red: make it fail

I wonder if the next method is the same as the one in test_pop_removes_and_returns_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 in test_dictionaries.py

    # 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 assertIsNone 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_and_returns_its_value(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_and_returns_its_value(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 assertIsNone to assertEqual

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': [0, 1, 2, 'n'],
    }
    

    the terminal shows AssertionError

    AssertionError: Tuples differ: ('keyN', [0, 1, 2, 'n']) != ('key', 'value')
    

    I change the expectation to match

    self.assertEqual(a_dictionary.popitem(), ('keyN', [0, 1, 2, 'n']))
    

    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', [0, 1, 2, 'n']))
    

    the terminal shows AssertionError

    AssertionError: Tuples differ: ('key1', 'value1') != ('keyN', [0, 1, 2, 'n'])
    

    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': [0, 1, 2, 'n'],
        }
        self.assertEqual(a_dictionary.popitem(), ('keyN', [0, 1, 2, 'n']))
        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_given_key_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 assertIsNone 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 assertIsNone to assertEqual

    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_given_key_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_given_key_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_making_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_iterable_of_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 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': [0, 1, 2, 'n'],
    }
    

    the terminal shows AssertionError

    AssertionError: Lists differ: ['value1', [0, 1, 2, 'n']] != ['value']
    

    I change the values in the expectation

    self.assertEqual(
        list(a_dictionary.values()),
        ['value1', [0, 1, 2, 'n']]
    )
    

    the test passes

  • I rename the test

    def test_values_of_a_dictionary(self):
        a_dictionary = {
            'key1': 'value1',
            'keyN': [0, 1, 2, 'n'],
        }
        self.assertEqual(
            list(a_dictionary.values()),
            ['value1', [0, 1, 2, 'n']]
        )
    
  • 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_and_returns_its_value 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