Heads up! To view this whole video, sign in with your Courses account or enroll in your free 7-day trial. Sign In Enroll
Preview
Start a free Courses trial
to watch this video
Doctests are the simplest tests to write in Python since they're written in plain text in the docstrings you're already writing for your code.
New terms
doctest - A test written in a docstring.
doctest library - The built-in Python library for running doctests.
Running doctests
From the command line
python -m doctest your_script.py
From inside a script
import doctest
doctest.testmod()
Doctests are something that's fairly
unique to Python.
0:00
Other languages have them, of course,
0:03
but they're not always as nicely
integrated as they are with Python.
0:05
Remember how, when we define a function or
class, we can write a docstring, or
0:08
a brief description of what the function
or class does?
0:12
Well inside that docstring you can also
write a test.
0:15
Let's go to Workspaces and check it out.
0:18
This script probably looks familiar to
you.
0:20
It's a slightly modified and
0:22
improved version of our Dungeon game from
the Python collection's course.
0:24
There are several functions in here, that
should be pretty easy to test,
0:28
through doctests.
0:33
So, let's see about adding some of those.
0:34
This also gives us a really great
opportunity to add
0:37
in explanatory docstrings to our functions
and to the file itself.
0:40
So, we can skip this first function.
0:45
We don't need to test it,
0:49
because all this function does is use
Python to call a system level command.
0:50
We know that the system-level thing is
gonna work and
0:54
we know that the Python function is gonna
work, or
0:57
rather, testing those two things is not
our responsibility.
0:59
We can rest assure that they work because
we know there are tests written for
1:04
them on the system and on Python itself.
1:07
So, we don't need to test it, but it
doesn't hurt to go ahead and
1:11
add in a docstring.
1:14
So, we'll just say, you know, clear the
screen.
1:17
Okay.
And then another thing often really good
1:22
to do is to add a,
1:24
effectively a docstring to your entire app
or to your entire script.
1:26
So, I'm going to add one up here that just
says dungeon game, explore a dungeon
1:32
to find a hidden door and escape.
1:39
But be careful!
1:44
The gru is hiding somewhere inside.
1:47
And then I'm going
1:53
to add a created 2014 updated 2015.
1:57
Oops, not 2016, and author Kenneth Love.
2:03
You can of course put your own name in
there, put whatever name you want.
2:09
You probably want to put, if you were
doing this for a script yourself,
2:12
you probably want to put in, like, dates
that you actually created these things.
2:16
But, I don't want to have to remember
exactly when [LAUGH] it was that I wrote
2:19
which thing.
2:23
So, anyway.
2:24
Just a bit of housekeeping,
2:25
this just makes it a little bit nicer for
somebody else who comes across this file.
2:26
Let's go add a test, though, to this next
function, this build cells function,
2:30
and, see how to write a doctest.
2:35
So, we're actually gonna come down here,
and we're gonna put in three quotes.
2:39
Just like we're writing a docstring,
2:43
doctests and docstrings are definitely
heavily related.
2:45
And let's say what this function does.
2:48
So it's going to create and return a width
2:49
by height grid of two tuples or sometimes
you'll see these called pairs.
2:55
So, now we need to write the test.
3:05
And I like to leave white space around my
tests.
3:08
It helps for letting Python find the
doctests.
3:11
Sometimes you'll get weird behavior if
there's not this
3:15
blank line at the end of the test.
3:18
And this blank line leaves a bit of nice
separation, so
3:20
you can see where the description ends and
where the tests begin.
3:23
So, the way that we write our tests
3:28
is any statement that we want Python to
run we start with three chevrons.
3:31
It looks like the Python shell.
3:36
So we want it to run cells equals build
cells, a width of two and a height of two.
3:39
And then I want to do len cells, and a two
by two grid should have four items in it.
3:46
So these are what we want to have happen,
these two lines,
3:54
and this is what we're expecting back from
Python.
3:59
So if we look at our function, we have
this cells list.
4:04
And then for each row that's in our height
and
4:08
then for each cell that's in our width,
4:12
we're going to append this pair of the two
numbers.
4:16
And then in the end we're going to return
our list.
4:21
So when we do this two-by-two, we should
get back four items.
4:24
Okay.
4:29
Looks an awful lot like the Python shell,
and we're actually going to, in a minute,
4:30
see how to build these things in the
Python shell, but
4:34
first let's write one more.
4:39
Let's go down here to this get locations.
4:40
And, it's going to get cells.
4:43
So, once again, we're gonna say,
4:45
randomly pick starting locations for
4:49
the monster, the door, and the player.
4:54
Where am I at?
5:00
I'm at 82.
5:01
All right, we'll go back.
5:01
There we go.
5:02
Okay.
5:04
So how would we go about testing this?
5:06
Well, let's look at the function does.
5:10
We've actually seen this one before, but
just let's go over it.
5:13
So we get a random place for the monster,
a random place for
5:15
the door, random place for the player.
5:17
If the monster is equal to the door, or
the monster is equal to the player, or
5:20
the door is equal to the player, then we
wanna get brand new locations for them.
5:23
Finally, we send those back.
5:26
You may not have noticed, but
5:29
this is actually a really good example of
a recursive function,
5:30
so some people are like, ooh, we need to
learn about recursive, or recursion.
5:33
Hey, here it is.
5:38
Okay, so let's write this.
5:39
So, first of all we're gonna do, oops, I
need my three chevrons,
5:41
cells equals build cells two by two.
5:44
We can use the other functions that are in
the file, so we're call our cells file,
5:49
ou, our build cells function up there, and
we're gonna say M, D and
5:53
P is equal to get locations for cells, and
5:57
then we're gonna make sure that M does not
equal D, and D does not equal P.
6:03
That should come back as true.
6:10
And then we're going to do D in cells just
to make sure it's in there.
6:12
And that should also come back as true.
6:16
Okay?
6:19
So let's save that.
6:19
Nothing too strange or weird in our test
there.
6:21
We're just kind of using it like we
normally would.
6:25
'Kay, let's look at our next function
here, get_moves(player).
6:27
This is a little bit more complicated than
these ones above.
6:31
We're doing a lotta checking in through
here.
6:36
So, let's actually see about writing this
one in the shell.
6:38
So let's add a couple of things in here
first.
6:44
Let's describe it.
6:47
We're going to say based on the tuple of
the player's position.
6:48
Return the list of acceptable moves.
6:56
[SOUND] Okay, so right here is where we
would start to write our stuff.
6:59
But I don't want to write it here, I want
to write it down here.
7:06
Okay.
7:10
So let's go into Python.
7:13
Oop, sorry, you know what, let's get out
of here.
7:14
Let's go into our dungeon directory.
7:16
And now go into Python.
7:19
Okay, from dd game import Get Moves, cause
that's something that we need to have.
7:22
And then if we look at Get Moves.
7:30
We see it's using this game dimensions
constant.
7:32
And if we look at the top of the file.
7:37
Where game dimensions is designed, it's
this tuple of five-five.
7:39
Let's let's define that our self so that
we don't have to use the five-five one.
7:44
So we'll say game dimensions is, let's do
two-two, let's stick to our small grid.
7:50
And then let's say get moves and
7:58
we'll pretend that our player is down in
the corner.
8:01
And we'll say zero is, is over in the
corner and we'll say zero, two.
8:03
So we run that and we get right up and
down.
8:07
Okay.
So that's pretty simple.
8:11
So what I'm going to actually do.
8:13
I don't need this from dd game, import get
moves line.
8:15
That's a given from my test.
8:18
But I do need this GAME_DIMENSIONS = (2,
2), get_moves, and that RIGHT, UP, DOWN.
8:20
So I've got that, I'm gonna copy it, I'm
gonna come,
8:27
let okay, let's exit out of here.
8:31
And then let's drop this down.
8:35
There we go.
8:39
Okay.
I'm gonna come up here.
8:40
And I'm gonna paste my stuff in and I'm
gonna tab it in, and I'm gonna save.
8:43
Hey look, there's my test.
8:48
I wrote the whole test.
8:51
Isn't that amazing?
8:53
I didn't even have to, like, I didn't have
to write it.
8:53
I just did it in the, I mean okay, I wrote
it in the shell.
8:55
But still, I didn't have to, come up with
what the answers were.
8:58
I could let the function just run and see
what happened.
9:02
Okay.
So we've got a few tests.
9:05
Let's see about running these.
9:07
Let's see what happens.
9:08
There's a way that we could run the test
directly from this file so
9:11
that when you run the file, it runs the
test.
9:14
But I'm not a really big fan of that
approach.
9:16
The main reason is because I don't always
want doctest to run.
9:19
In fact, I usually don't want doc test to
run.
9:22
I will list how to do that in the
teacher's notes though.
9:25
So instead, let's actually just run it
explicitly down here in our shell.
9:28
So, what we can do is we do Python dash M
doctest, and then we do DD game.py.
9:33
So what is this Dash M.
9:40
So the dash M tells Python to load the
doctest module.
9:43
And the doctest module has some special
code written in it
9:47
that says you're going to give me a file
and when you give me that file,
9:52
I'm going to look through that file for
doctests.
9:56
And I'm going to find the doctests and I'm
going to run them.
9:59
So, that's what we've done.
10:02
We've said, load the doctest module and
then give it the dd_game.py file.
10:03
Nothing comes back.
10:11
Guess what?
10:12
That's a good thing.
10:13
Nothing comes back.
10:14
That means everything passed.
10:15
That's exactly what we want but, let's
make on fail real quick.
10:16
Let's go right here to this one.
10:20
We know that right, up, and down are gonna
come out.
10:21
So to make it fail let's get rid of up.
10:25
Okay, let's say up is not gonna come up.
10:28
We're just gonna get right and down.
10:30
Okay?
10:32
So we come down here and we run our test
again.
10:33
And here's our fail, so our fail is
actually really, really good.
10:37
I'm gonna run this one more time so we can
see the whole thing.
10:40
Here we go.
10:43
So, this file failed on line 64.
10:44
So, really, we failed on line 65, but,
10:47
line 64 is the one that we ran and it
didn't give us what we expected.
10:51
So, the failure, 65 has the wrong content.
10:56
64 is the line that gave us the wrong
content, so
11:00
64 is where the failure happened.
11:03
So, we ran this.
11:06
We expected this, right?
11:08
But we got this.
11:12
So, to fix this test, we need to come back
in here and add in up.
11:14
If we save and we run it again,
11:20
now, there's no output because all the
tests are good.
11:22
I'm not going to write anymore doctests
for this function, or for
11:26
these functions in this script.
11:29
If you want more practice, then I suggest
you do it.
11:31
Go in and write doctests for all the rest
of them.
11:33
You should be able to do it no problem.
11:35
Wow, doctests are really simple to
implement, and run.
11:37
We should definitely write these when
we're planning our code.
11:41
It's a lot simpler to write a doctest than
a full test suite, especially for
11:43
simple sanity checks.
11:46
Or, to write tests before you start
coding.
11:48
Doctests do have some serious shortcomings
though.
11:50
They're pretty tightly bound to the code
they're written on, so
11:53
you can't just reuse them for new
functions or classes, and
11:55
all of their checking is done through
string comparison, so
11:59
sometimes floats can be tricky or even
impossible to test with doctests.
12:01
Since doctest won't solve all of our
problems,
12:06
let's look at a more powerful solution,
the unittest module.
12:08
You need to sign up for Treehouse in order to download course files.
Sign up