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 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 programming I have to deal 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 change True to False to make the test pass


test_making_a_dictionary

red: make it fail

I change test_failure

class TestDictionaries(unittest.TestCase):

    def test_making_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_making_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_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 as a key in a dictionary


test_making_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_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 as a key

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 a boolean as a key in a dictionary


test_making_a_dictionary_w_a_number_as_a_key

red: make it fail

I add a failing test

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 add a float as a key

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 integers and floats as keys in a dictionary


test_making_a_dictionary_w_a_tuple_as_a_key

red: make it fail

I add a test for a tuple as a key

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 a tuple as a key in a dictionary


test_making_a_dictionary_w_a_list_as_a_key

red: make it fail

I add another test

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'

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_making_a_dictionary_w_a_list_as_a_key(self):
    {[3, 2, 1]: 'BOOM!'}

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 make a dictionary with a list as a key


test_making_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_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 set as a key in a dictionary


test_making_a_dictionary_w_a_dictionary_as_a_key

red: make it fail

I add a new 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 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 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 extra characters

Note

results can be different because of 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 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 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

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

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

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

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

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 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_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 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(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 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': '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 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_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_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_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': '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