what is an assertion?
An assertion or assert statement is me telling the computer “DO NOT CONTINUE, IF this statement IS NOT TRUE”
I use assertions when making a program to make sure something is True before the computer can continue running the program.
I also use them to test how the program behaves, for example when it is given inputs.
Assertions can help catch things that break passing tests when I add new lines of code. They also help me answer 2 questions
what is the same?
what is different?
The difference between my expectations (tests and ideas) and reality (what happens when the program runs), tells me what to change to make them match. The closer my program is to reality, the better.
preview
These are the tests I have by the end of the chapter
1import unittest
2
3
4class TestAssertionError(unittest.TestCase):
5
6 def test_what_is_an_assertion(self):
7 assert 1 + 1 == 2
8 self.assertEqual(1+1, 2)
9
10 assert '1' + '1' == '11'
11 self.assertEqual('1'+'1', '11')
12
13 assert 'I am' + ' a programmer' == 'I am a programmer'
14 self.assertEqual('I am'+' a programmer', 'I am a programmer')
15
16 def test_assertion_error_w_none(self):
17 assert None is None
18 self.assertIsNone(None)
19
20 assert False is not None
21 self.assertIsNotNone(False)
22
23 assert True is not None
24 self.assertIsNotNone(True)
25
26 def test_assertion_error_w_false(self):
27 assert True is not False
28 self.assertFalse(False)
29
30 def test_assertion_error_w_true(self):
31 assert False is not True
32 self.assertTrue(True)
33
34 def test_assertion_error_w_equality(self):
35 assert None == None
36 self.assertEqual(None, None)
37
38 assert False != None
39 self.assertNotEqual(False, None)
40
41 assert True != None
42 self.assertNotEqual(True, None)
43
44 assert True != False
45 self.assertNotEqual(True, False)
46
47 assert False == False
48 self.assertEqual(False, False)
49
50 assert False != True
51 self.assertNotEqual(False, True)
52
53 assert True == True
54 self.assertEqual(True, True)
55
56
57# NOTES
58# True is True and equal to True
59# False is not True and not equal to True
60# False is False and equal to False
61# True is not False and not equal to False
62# True is not None and not equal to None
63# False is not None and not equal to None
64# None is None and equal to None
65
66
67# Exceptions seen
68# AssertionError
questions about AssertionError
Here are questions you can answer after going through this chapter
start the project
I name this project
assertion_errorI open a terminal
I make a directory for the project
mkdir assertion_errorthe terminal goes back to the command line
.../pumping_pythonI change directory to the project
cd assertion_errorthe terminal shows I am in the
assertion_errorfolder.../pumping_python/assertion_errorI make a directory for the tests
mkdir teststhe terminal goes back to the command line
I make the
testsdirectory a Python packageDanger
use 2 underscores (__) before and after
initfor__init__.pynot_init_.pytouch tests/__init__.pyNote
on Windows without Windows Subsystem for Linux use
New-Item tests/__init__.pynottouch tests/__init__.pyNew-Item tests/__init__.pythe terminal goes back to the command line
I make a Python file for the tests in the
testsdirectorytouch tests/test_assertion_error.pyNote
on Windows without Windows Subsystem for Linux use
New-Item tests/test_assertion_error.pynottouch tests/test_assertion_error.pyNew-Item tests/test_assertion_error.pythe terminal goes back to the command line
I open
test_assertion_error.pyin the editor of the Integrated Development Environment (IDE)Tip
I can use the terminal to open a file in the Integrated Development Environment (IDE) by typing the name of the program and the name of the file. That means when I type this in the terminal
code tests/test_assertion_error.pyVisual Studio Code opens
test_assertion_error.pyin the editorI add the first failing test to
test_assertion_error.py1import unittest 2 3 4class TestAssertionError(unittest.TestCase): 5 6 def test_failure(self): 7 self.assertFalse(True)I make a requirements file for the Python packages I need, in the terminal
echo "pytest" > requirements.txtthe terminal goes back to the command line
I add pytest-watcher to the file
echo "pytest-watcher" >> requirements.txtthe terminal goes back to the command line
I setup the project with uv
uv initthe terminal shows
Initialized project `assertion-error`I remove
main.pyfrom the project because I do not use itrm main.pythe terminal goes back to the command line
I install the Python packages I gave in the requirements file
uv add --requirement requirements.txtthe terminal shows it installed the Python packages
I use pytest-watcher to run the tests automatically
uv run pytest-watcher . --nowthe terminal shows
================================ FAILURES ================================ ____________________ TestAssertionError.test_failure _____________________ self = <tests.test_assertion_error.TestAssertionError testMethod=test_failure> def test_failure(self): > self.assertFalse(True) E AssertionError: True is not false tests/test_assertion_error.py:7: AssertionError ======================== short test summary info ========================= FAILED tests/test_assertion_error.py::TestAssertionError::test_failure - AssertionError: True is not false =========================== 1 failed in X.YZs ============================I add AssertionError to the list of Exceptions seen in
test_assertion_error.pyin the editor4class TestAssertionError(unittest.TestCase): 5 6 def test_failure(self): 7 self.assertFalse(True) 8 9 10# Exceptions seen 11# AssertionErrorthen I change True to False in the assertion
7 self.assertFalse(False)the test passes
test_what_is_an_assertion
We know that the result of 1 + 1 is 2, but what if I said that '1' + '1' is '11', would you agree?
I can use assertions to make the computer check if these statements are True
RED: make it fail
I change
test_failuretotest_what_is_an_assertionwith the first statement1import unittest 2 3 4class TestAssertionError(unittest.TestCase): 5 6 def test_what_is_an_assertion(self): 7 1 + 1 == 2 8 9 10# Exceptions seen 11# AssertionErrorNote
==is 2 equal signs - =+= and meansis equalwhich makes this statement read as1 + 1 is equal to 2. The terminal still shows greenI change the statement to make it False
7 1 + 1 == 11the terminal still shows green.
I want the test to fail when I write a statement that is not True. I change it to an assert statement
7 assert 1 + 1 == 11the terminal shows AssertionError
E assert (1 + 1) == 11
GREEN: make it pass
I change the statement to make it True
7 assert 1 + 1 == 2
the test passes
REFACTOR: make it better
I add another statement
6 def test_what_is_an_assertion(self): 7 assert 1 + 1 == 2 8 '1' + '1' == '2' 9 10 11# Exceptions seenthe test is still green
I add assert before the statement
8 assert '1' + '1' == '2'the terminal shows AssertionError
E AssertionError: assert '11' == '2' E E - 2 E + 11- 2shows my expectation - what I wrote as the result+ 11shows reality - what the actual result is
I change the statement to make it True
8 assert '1' + '1' == '11'the test passes
Note
these 2 statements are not the same
I add another statement to show the difference between the statements
6 def test_what_is_an_assertion(self): 7 assert 1 + 1 == 2 8 assert '1' + '1' == '11' 9 'I am' + ' a programmer' == '11' 10 11 12# Exceptions seenthe test is still green
I change the statement to an assertion
9 assert 'I am' + ' a programmer' == '11'the terminal shows AssertionError
E AssertionError: assert 'I am a programmer' == '11' E E - 11 E + I am a programmerthe
- 11shows my expectation - what I wrote as the resultthe
+ I am a programmershows reality - what the actual result is
-
4class TestAssertionError(unittest.TestCase): 5 6 def test_what_is_an_assertion(self): 7 assert 1 + 1 == 2 8 assert '1' + '1' == '11' 9 assert 'I am' + ' a programmer' == 'I am a programmer' 10 11 12# Exceptions seenthe test passes
what causes AssertionError?
AssertionError happens when the statement after assert is False. It was in how to make a python test driven development environment with the first failing test
self.assertFalse(True)
which is like this assertion
assert True is False
With these statements, I am telling the computer “CONTINUE ONLY if True is False”. I expect this line to fail because True is not False, they at least have different spellings. If it does not fail, then Python and I have a problem
test_assertion_error_w_none
None is used when there is no value, it is the simplest data structure in Python. I can use assertions to test if something is None, this is useful when I want to check what value I am getting from some process or user input.
For example,
if I have a process that changes something but does not return a value, I can use an assertion to make sure that the process returns None
if I have people fill a form and I want to test for when they do not put anything in a place that needs something
RED: make it fail
I add a new failing test
6 def test_what_is_an_assertion(self): 7 assert 1 + 1 == 2 8 assert '1' + '1' == '11' 9 'I am' + ' a programmer' == '11' 10 11 def test_assertion_error_w_none(self): 12 assert None is not None 13 14 15# Exceptions seenthe terminal shows AssertionError
E assert None is not Nonethe assert statement is False
GREEN: make it pass
I change the line to make it True
12 assert None is None
the test passes
REFACTOR: make it better
I can also make assertions with assert methods from the unittest.TestCase class, they have clearer messages
how to test if something is NOT None
I add a failing line with the assertIsNotNone method which checks if the thing in parentheses - ( ), is NOT None
11 def test_assertion_error_w_none(self):
12 assert None is None
13 self.assertIsNotNone(None)
the terminal shows AssertionError with a clearer message
AssertionError: unexpectedly None
how to test if something is None
I change the statement to use the assertIsNone method which checks if the thing in parentheses is None
11 def test_assertion_error_w_none(self): 12 assert None is None 13 self.assertIsNone(None)the test passes. I like the messages from the assert methods better
I add comments
11 def test_assertion_error_w_none(self): 12 assert None is None 13 self.assertIsNone(None) 14 15 16# NOTES 17# None is None 18 19 20# Exceptions seen 21# AssertionError
I add a new assertion to compare None with False, another simple data structure
11 def test_assertion_error_w_none(self): 12 assert None is None 13 self.assertIsNone(None) 14 15 assert False is None 16 17 18# NOTESthe terminal shows AssertionError
E assert False is NoneI change the assert statement to make it True
15 assert False is not Nonethe test passes
I add another assertion with the assertIsNone method
11 def test_assertion_error_w_none(self): 12 assert None is None 13 self.assertIsNone(None) 14 15 assert False is not None 16 self.assertIsNone(False)the terminal shows AssertionError
AssertionError: False is not NoneI change assertIsNone to assertIsNotNone to make the assertion True
16 self.assertIsNotNone(False)the test passes
I add a comment
15 assert False is not None 16 self.assertIsNotNone(False) 17 18 19# NOTES 20# False is not None 21# None is None 22 23 24# Exceptions seen 25# AssertionError
I add an assertion to compare None with True, another simple data structure
11 def test_assertion_error_w_none(self): 12 assert None is None 13 self.assertIsNone(None) 14 15 assert False is not None 16 self.assertIsNotNone(False) 17 18 assert True is None 19 20 21# NOTESthe terminal shows AssertionError
E assert True is NoneI change the assert statement to make it True
18 assert True is not Nonethe test passes
I add a failing line with the assertIsNone method
18 assert True is not None 19 self.assertIsNone(True)the terminal shows AssertionError
AssertionError: True is not NoneI make the statement True with assertIsNotNone
18 def test_assertion_error_w_none(self): 19 assert None is None 20 self.assertIsNone(None) 21 22 assert False is not None 23 self.assertIsNotNone(False) 24 25 assert True is not None 26 self.assertIsNotNone(True) 27 28 29# NOTESthe test passes
I add a comment
18 assert True is not None 19 self.assertIsNotNone(True) 20 21 22# NOTES 23# True is not None 24# False is not None 25# None is None 26 27 28# Exceptions seen 29# AssertionError
test_assertion_error_w_false
False is a simple data type, it is one of the two booleans and is NOT None. I can use assertions to test if something is False or not.
RED: make it fail
I add a failing test
11 def test_assertion_error_w_none(self):
12 assert None is None
13 self.assertIsNone(None)
14
15 assert False is not None
16 self.assertIsNotNone(False)
17
18 assert True is not None
19 self.assertIsNotNone(True)
20
21 def test_assertion_error_w_false(self):
22 assert True is False
23
24
25# NOTES
the terminal shows AssertionError
E assert True is False
GREEN: make it pass
I change the assertion to make it True
22 assert True is not False
the test passes
REFACTOR: make it better
There is an assert method to check if something is False, it is the one from the first failing test
how to test if something is False
I add a failing line with the assertFalse method
21 def test_assertion_error_w_false(self): 22 assert True is not False 23 self.assertFalse(True)the terminal shows AssertionError
AssertionError: True is not falseI change the assert statement to make it True
23 self.assertFalse(False)the test passes
I add comments
21 def test_assertion_error_w_false(self): 22 assert True is not False 23 self.assertFalse(False) 24 25 26# NOTES 27# False is False 28# True is not False 29# True is not None 30# False is not None 31# None is None 32 33 34# Exceptions seen
test_assertion_error_w_true
True is a simple data type, it is the other boolean and is NOT False or None. I can use assertions to test if something is True or not.
RED: make it fail
I add a failing test
21 def test_assertion_error_w_false(self):
22 assert True is not False
23 self.assertFalse(False)
24
25 def test_assertion_error_w_true(self):
26 assert False is True
27
28
29# NOTES
the terminal shows AssertionError
E assert False is True
GREEN: make it pass
I change the assert statement to make it True
26 assert False is not True
the test passes
REFACTOR: make it better
There is an assert method to check if something is True
how to test if something is True
I add a failing assertion with the assertTrue method
25 def test_assertion_error_w_true(self): 26 assert False is not True 27 self.assertTrue(False)the terminal shows AssertionError
AssertionError: False is not trueI change the assertion to make it True
25 def test_assertion_error_w_true(self): 26 assert False is not True 27 self.assertTrue(True) 28 29 30# NOTESthe test passes
I add more comments
25 def test_assertion_error_w_true(self): 26 assert False is not True 27 self.assertTrue(True) 28 29 30# NOTES 31# True is True 32# False is not True 33# False is False 34# True is not False 35# True is not None 36# False is not None 37# None is None
I can use assertions to test if something is True or NOT
All the assertions I have typed so far show that, True, False and None are different. They give me a basic expectation of Python because I can compare things with them.
test_assertion_error_w_equality
I can use assertions to test if 2 things are equal, like I did with test_what_is_an_assertion
RED: make it fail
I add a new failing test
25 def test_assertion_error_w_true(self):
26 assert False is not True
27 self.assertTrue(True)
28
29 def test_assertion_error_w_equality(self):
30 assert None != None
31
32
33# NOTES
the terminal shows AssertionError
E assert None != None
Note
!= is !+= on the keyboard and is the symbol for NOT equal which makes this statement read as assert None is NOT equal to None or “CONTINUE only if None is NOT equal to None”
GREEN: make it pass
I change the assertion to make it True
30 assert None == None
the test passes
REFACTOR: make it better
what is the difference between = and ==?
=is the symbol for assignment or pointing, it’s how to give a name to something in Python, for exampleproject_name = 'assertion_error'any time I use
project_nameafter writing that line, Python will replace it withassertion_error==checks if the thing on the left of==is equal to the thing on the right of==, for example'thing on the left' == 'thing on the right'
how to test if two things are NOT Equal
There are assert methods to check if 2 things are equal or not.
I add assertNotEqual which checks if the 2 things in the parentheses are NOT equal
29 def test_assertion_error_w_equality(self): 30 assert None == None 31 self.assertNotEqual(None, None)the terminal shows AssertionError
AssertionError: None == None
how to test if two things are Equal
I change assertNotEqual to assertEqual which checks if the 2 things in the parentheses are equal
29 def test_assertion_error_w_equality(self): 30 assert None == None 31 self.assertEqual(None, None) 32 33 34# NOTESthe test passes
I add to the
None is Nonecomment34# NOTES 35# True is True 36# False is not True 37# False is False 38# True is not False 39# True is not None 40# False is not None 41# None is None and equal to None 42 43 44# Exceptions seen 45# AssertionErrorI add a new failing assertion to compare False with None
29 def test_assertion_error_w_equality(self): 30 assert None == None 31 self.assertEqual(None, None) 32 33 assert False == Nonethe terminal shows AssertionError
E assert False == NoneI change the assert statement to make it True
33 assert False != Nonethe test passes
I add a failing assertion with the assertEqual method
29 def test_assertion_error_w_equality(self): 30 assert None == None 31 self.assertEqual(None, None) 32 33 assert False != None 34 self.assertEqual(False, None)the terminal shows AssertionError
AssertionError: False != NoneI make the statement True with assertNotEqual
29 def test_assertion_error_w_equality(self): 30 assert None == None 31 self.assertEqual(None, None) 32 33 assert False != None 34 self.assertNotEqual(False, None) 35 36 37# NOTESthe test passes
I add to the
False is not Nonecomment37# NOTES 38# True is True 39# False is not True 40# False is False 41# True is not False 42# True is not None 43# False is not None and not equal to None 44# None is None and equal to None 45 46 47# Exceptions seen 48# AssertionErrorI add the next failing assertion
29 def test_assertion_error_w_equality(self): 30 assert None == None 31 self.assertEqual(None, None) 32 33 assert False != None 34 self.assertNotEqual(False, None) 35 36 assert True == Nonethe terminal shows AssertionError
E assert True == NoneI change the assert statement to make it True
36 assert True != Nonethe test passes
I add a failing assertion with assertEqual
29 def test_assertion_error_w_equality(self): 30 assert None == None 31 self.assertEqual(None, None) 32 33 assert False != None 34 self.assertNotEqual(False, None) 35 36 assert True != None 37 self.assertEqual(True, None)the terminal shows AssertionError
AssertionError: True != NoneI make the assertion True with assertNotEqual
33 assert False != None 34 self.assertNotEqual(False, None) 35 36 assert True != None 37 self.assertNotEqual(True, None) 38 39 40# NOTESthe test passes
I add to the
True is not Nonecomment40# NOTES 41# True is True 42# False is not True 43# False is False 44# True is not False 45# True is not None and not equal to None 46# False is not None and not equal to None 47# None is None and equal to None 48 49 50# Exceptions seen 51# AssertionErrorI add another failing assertion
29 def test_assertion_error_w_equality(self): 30 assert None == None 31 self.assertEqual(None, None) 32 33 assert False != None 34 self.assertNotEqual(False, None) 35 36 assert True != None 37 self.assertNotEqual(True, None) 38 39 assert True == Falsethe terminal shows AssertionError
E assert True == FalseI change the assert statement to make it True
39 assert True != Falsethe test passes
I add a failing assertion with assertEqual
29 def test_assertion_error_w_equality(self): 30 assert None == None 31 self.assertEqual(None, None) 32 33 assert False != None 34 self.assertNotEqual(False, None) 35 36 assert True != None 37 self.assertNotEqual(True, None) 38 39 assert True != False 40 self.assertEqual(True, False)the terminal shows AssertionError
AssertionError: True != FalseI change the assert method to assertNotEqual
36 assert True != None 37 self.assertNotEqual(True, None) 38 39 assert True != False 40 self.assertNotEqual(True, False) 41 42 43# NOTESthe test passes
I add to the
True is not Falsecomment43# NOTES 44# True is True 45# False is not True 46# False is False 47# True is not False and not equal to False 48# True is not None and not equal to None 49# False is not None and not equal to None 50# None is None and equal to None 51 52 53# Exceptions seen 54# AssertionErroron to the next failing assertion
36 assert True != None 37 self.assertNotEqual(True, None) 38 39 assert True != False 40 self.assertNotEqual(True, False) 41 42 assert False != False 43 44 45# NOTES 46# True is True 47# False is not Truethe terminal shows AssertionError
E assert False != False-
42 assert False == Falsethe test passes
I add another failing assert statement with assertNotEqual
42 assert False == False 43 self.assertNotEqual(False, False)the terminal shows AssertionError
AssertionError: False == FalseI change assertNotEqual to assertEqual
39 assert True != False 40 self.assertNotEqual(True, False) 41 42 assert False == False 43 self.assertEqual(False, False) 44 45 46# NOTES 47# True is True 48# False is not Truethe test passes
I add to the
False is Falsecomment46# NOTES 47# True is True 48# False is not True 49# False is False and equal to False 50# True is not False and not equal to False 51# True is not None and not equal to None 52# False is not None and not equal to None 53# None is None and equal to None 54 55 56# Exceptions seen 57# AssertionError
I add a failing assert statement
39 assert True != False 40 self.assertNotEqual(True, False) 41 42 assert False == False 43 self.assertEqual(False, False) 44 45 assert False == True 46 47 48# NOTES 49# True is True 50# False is not Truethe terminal shows AssertionError
E assert False == TrueI change the assertion to make it True
45 assert False != Truethe test passes
I add another failing line with the assertEqual method
42 assert False == False 43 self.assertEqual(False, False) 44 45 assert False != True 46 self.assertEqual(False, True) 47 48 49# NOTES 50# True is True 51# False is not Truethe terminal shows AssertionError
AssertionError: False != TrueI make the statement True with assertNotEqual
45 assert False != True 46 self.assertNotEqual(False, True) 47 48 49# NOTESthe test passes
I add to the
False is not Truecomment49# NOTES 50# True is True 51# False is not True and not equal to True 52# False is False and equal to False 53# True is not False and not equal to False 54# True is not None and not equal to None 55# False is not None and not equal to None 56# None is None and equal to None 57 58 59# Exceptions seen 60# AssertionError
time for the last assert statements
45 assert False != True 46 self.assertNotEqual(False, True) 47 48 assert True != True 49 50 51# NOTES 52# True is True 53# False is not True and not equal to Truethe terminal shows AssertionError
E assert True != True-
48 assert True == Truethe test passes
I add a failing line with the assertNotEqual method
45 assert False != True 46 self.assertNotEqual(False, True) 47 48 assert True == True 49 self.assertNotEqual(True, True) 50 51 52# NOTES 53# True is True 54# False is not True and not equal to Truethe terminal shows AssertionError
AssertionError: True == TrueI change the assertNotEqual method to the assertEqual method
29 def test_assertion_error_w_equality(self): 30 assert None == None 31 self.assertEqual(None, None) 32 33 assert False != None 34 self.assertNotEqual(False, None) 35 36 assert True != None 37 self.assertNotEqual(True, None) 38 39 assert True != False 40 self.assertNotEqual(True, False) 41 42 assert False == False 43 self.assertEqual(False, False) 44 45 assert False != True 46 self.assertNotEqual(False, True) 47 48 assert True == True 49 self.assertEqual(True, True) 50 51 52# NOTESthe test passes
I add to the
True is Truecomment52# NOTES 53# True is True and equal to True 54# False is not True and not equal to True 55# False is False and equal to False 56# True is not False and not equal to False 57# True is not None and not equal to None 58# False is not None and not equal to None 59# None is None and equal to None 60 61 62# Exceptions seen 63# AssertionError
I add calls to the assertEqual method in test_what_is_an_assertion
4class TestAssertionError(unittest.TestCase): 5 6 def test_what_is_an_assertion(self): 7 assert 1 + 1 == 2 8 self.assertEqual(1+1, 3) 9 10 assert '1' + '1' == '11' 11 assert 'I am' + ' a programmer' == 'I am a programmer' 12 13 def test_assertion_error_w_none(self):the terminal shows AssertionError
AssertionError: 2 != 3I change the expectation in the call to the assertEqual method to make the assertion True
8 self.assertEqual(1+1, 2)the test passes
I add assertEqual for the next assertion
6 def test_what_is_an_assertion(self): 7 assert 1 + 1 == 2 8 self.assertEqual(1+1, 2) 9 10 assert '1' + '1' == '11' 11 self.assertEqual('1'+'1', '2') 12 13 assert 'I am' + ' a programmer' == 'I am a programmer'the terminal shows
AssertionError: '11' != '2'I change the expectation to match reality
11 self.assertEqual('1'+'1', '11')the test passes
I add assertEqual for the last assertion in test_what_is_an_assertion
6 def test_what_is_an_assertion(self): 7 assert 1 + 1 == 2 8 self.assertEqual(1+1, 2) 9 10 assert '1' + '1' == '11' 11 self.assertEqual('1'+'1', '11') 12 13 assert 'I am' + ' a programmer' == 'I am a programmer' 14 self.assertEqual('I am'+' a programmer', '11')the terminal shows AssertionError
AssertionError: 'I am a programmer' != '11'I make the expectation match reality
6 def test_what_is_an_assertion(self): 7 assert 1 + 1 == 2 8 self.assertEqual(1+1, 2) 9 10 assert '1' + '1' == '11' 11 self.assertEqual('1'+'1', '11') 12 13 assert 'I am' + ' a programmer' == 'I am a programmer' 14 self.assertEqual('I am'+' a programmer', 'I am a programmer') 15 16 def test_assertion_error_w_none(self):the test passes
close the project
review
I can use assert statements and assert methods to test if something is
NOT None with assertIsNotNone
None with assertIsNone
and to test if 2 things are
NOT Equal with assertNotEqual
Equal with assertEqual
for a total of 6 assert methods I can use when testing
code from the chapter
what is next?
Congratulations! You now know
rate pumping python
If this has been a 7 star experience for you, please CLICK HERE to leave a 5 star review of pumping python. It helps other people get into the book too