-
Okay, so we're gonna start with this very
hands on approach to testing. Testing is a
-
thing that I, I have to admit it's really
just three or four years ago that I got
-
religion about it, and what I mean by that
is I always sort of, you know, I was told
-
it was important, I believed it was
important, I kinda tried to do it but
-
doing it this way really changed my life
and I hope it will change yours too and
-
no, I'm not on the presidential campaign,
this is a, anyway, okay So, let's start
-
talking about unit tests. There's some
background in the book about the different
-
kinds of testing. We'll focus initially on
unit tests and a little bit on functional
-
tests and, as with so many things in this
class, there's a handy acronym that helps
-
us remember what good unit tests should
be. So one of them is they should be fast,
-
shouldn't take a long time to run them.
They should be independent, that means
-
that running one test before another
shouldn't make any difference. The order
-
in which you run them shouldn't have
dependencies on each other. They should be
-
repeatable, if a test finds a bug it
should find that bug every time. In some
-
cases it's easy to do this, in other cases
it's surprisingly subtle. Self-checking.
-
What testing used to mean, not that long
ago for many companies, is that software
-
got thrown over the wall to this QA
department. And then people in QA would
-
sort of manually poke at the software and
do things. And yep that work. Yep, that
-
worked. That, we don't do that anymore,
right? The test code has to know itself if
-
it passed or failed. No human intervention
should be required to make that decision.
-
And timely. That means that the test
should be written right around the same
-
time the code was written. If the code
changes, the test change right away. In
-
fact, we're actually gonna do it even more
aggressively. We're gonna write the test
-
first, before the code is written. So
that's as timely as you can be. It's like
-
time warp delivery. So, what does this
mean? Because, if tests are fast, why do
-
you want that? Well, it's because you can
run a subset of the tests all the time. If
-
you have thousands and thousands of unit
tests, which is not uncommon, even in a
-
medium sized project. It could take, you
know, a minute or two to run the entire
-
test sweep, and that really slows you
down. What you want is to be able to
-
quickly run just the tests that apply to
the piece of code you're working on, and
-
not have that sort of take you out of
rhythm. Independent. For that same reason,
-
you want to be able to run any subset and
you want to be able to run them in any
-
order. So, it would be bad if there was a
set of tests that only worked properly
-
provided you ran some other tests ahead of
them. Repeatable. You know, again, run it
-
N times, get the same results. If you
wanna isolate bugs, and enable automatic
-
debugging, repeatability is essential.
Self-checking, like I said. No human
-
checking of the output. This means that
you can sort of have tests running in the
-
background all the time. And whenever you
change something that breaks something 25
-
miles away in another piece of code, some
tests will detect that fact, and pick it
-
up and bring it to your attention. And
timely, like I said, we're going to use
-
test-driven development, where we write
the test before we write the code. So,
-
acronym heaven. We're going to be using
our spec, which I think of as a domain
-
specific language for writing tests. For
those of you who aren't familiar with
-
DSL's, it's basically like a small
programming language that only does a
-
small number of things within one task
domain. So it doesn't try to be general,
-
and we've actually seen examples of them
so far. Migrations are kind of a DSL,
-
right. There's a, a small set of
statements whose sole job is to express
-
changes to the database schema. Now
migrations happen to be a D.S.L. That is
-
embedded in Ruby. Meaning, migrations are
just Ruby code, but they're stylized to
-
look like the tasks they do. And, in fact,
we'll see that our spec is a, a similar
-
example. So we'll call those an internal
DSL. It's implemented inside of another
-
language. Regular expressions are another
internal DSL, right? There's, like, this
-
little sub vocabulary of things you can do
in regular expressions. Different example
-
of an external or a stand-alone DSL is
SQL. Sequel queries for databases, right,
-
that, it's own language and those of you
who have worked with other frameworks
-
before coming to Rails, usually what you
end up doing is essentially writing sequel
-
queries and then passing off a string to
someone, right? So that's a very clear
-
example of dealing with different
languages. [cough] So in R spec, each test
-
is called a spec, for a specification.
Surprisingly, they inhabit a directory
-
called spec because we like to keep things
simple. And there's a rails generator, R
-
spec install that creates the sub
directory structure. So this is all in the
-
book and we'll assume for some of the live
demos we're doing today that we've kind of
-
done these set up steps. So where do
things go? The spec directories are
-
designed to basically mimic where things
go in your applications, so you know in
-
apps/models you have your models, in
spec/models you have a spec file for each
-
model, not surprising. Similarly for
controllers we have controller specs, and
-
what about views? Well we're not really
gonna do view specs, it's possible to do
-
them, but they're a little bit important
to write, a lot of what you want to check
-
in a view is stuff that you can actually
do in a controller spec which we'll see an
-
example of today. And besides, we've
decided that our push for writing these
-
user-facing web apps is we're doing user
stories to express the parts of the app
-
that the customer can directly interact
with. So, things that are part of the
-
view, what should be visible in the view
and what should be clickable and so forth,
-
we've been using Cucumber for that and
we're gonna continue to do so. So for the
-
most part we're gonna restrict our
attention and our spec to writing specs
-
for our models and specs for our
controllers. So we'll start with an
-
example of a new hypothetical feature for
rotten potatoes, where we can add movies
-
using information gleaned from TMDB. Tmdb
is a real site. It is a lot like IMDB. But
-
it's a non-commercial, sort of open source
one. And the idea is that they have all
-
this information about movies. So if we
want to add movies to rotten potatoes? Why
-
don't we just scrape the information from
there? And in fact, when you, talked about
-
user stories, there is a, a step in one of
the user stories that says, I'm filling in
-
search terms. I'm gonna search for the
movie, Inception. And then I press this
-
button that says search TMDB, right? So
that's supposed to be the button that will
-
somehow go off to TMDB, and see if
Inception is in there, and pull it back.
-
So. The question is: What is? What do we
need to do? What kind of code needs to be
-
written? And what kind of testing is
entailed by getting this statement that is
-
in red to work? And before we launch into
this, remember when we talked about Rails
-
Cookery? Sort of recipes for doing things
in rails? Remember that when we add any
-
kind of new feature that means we need a
new route. We need a new controller
-
method. And we may or may not need a new
view depending on whether the feature can
-
re-use an existing view or, or we have to
do something new. But these are the steps
-
we always follow. So let's start with
these one at a time. Okay so, this is an
-
idea that we're gonna see many, many
times. I'll just get to use to the phrase.
-
To the code you wish you had. It is an
immensely powerful idea once you get used
-
to do. It feels weird to do things this
way when you start but it's very powerful.
-
So we ask ourselves okay when the user
clicks on that button for search TMDB. We
-
know that somewhere there's going to be a
controller action that receives whatever
-
that form submission was. So what should
that method do? What should the controller
-
method do that receives the search form?
Well, if you just kind of ask, you know,
-
in English, if we wrote down what it
should do, we'd say well, let's see. It
-
should call some method, which we haven't
written yet, that will actually go out to
-
TMDB and actually search for the movie.
Make sense? If it finds a movie, it should
-
render some kind of a view with the search
results for displaying the match. Again,
-
we haven't created that view, but
logically this is just what we're going to
-
write down that it should do. And we're
not going to have time to do a step three
-
today, but this sort of sad path, is if it
doesn't match, if you're redirect to rock
-
potatoes home page and say I didn't find
anything. And if you look through the
-
example in the book, we actually did the
sad path in the BDD chapter. So, given
-
that these two things, lets focus on
number one and number two, are the things
-
that the controller method should do,
here's how we would express. Where's my
-
mouse? There we go. Here's how we would
express those should. Using R spec and,
-
okay, so not a lot going on here and, you
know it's pretty straight forward, right?
-
And this is legal R spec code, Spec Helper
is just a file that R spec creates as part
-
of that install step. It does some
housekeeping things to make sure
-
everything's loaded up. And we're gonna
start by saying, what is this test about?
-
Are we gonna describe the behavior of the
movies controller? And the controller is
-
gonna have a lot of different behaviors,
but the one that we're concerned about
-
here is the behavior of searching TMVB. So
you can imagine for each different
-
behavior in the controller as our spec
grows we're gonna add more describe blocks
-
inside of this, this outer most one
describe movies controller and you can
-
keep nesting, nesting, nesting. And all I
did here was transcribe. Three things that
-
we said it should do. So it is actually a
method called R spec and it takes an
-
argument, which is a string describing,
what should happen. And as we'll see in a
-
moment, it also takes a second argument,
which is a procedure that does that actual
-
task. But for now, all we've done is
transliterate. We've thought of three
-
things the controller method should do. We
wrote those three things in our spec. This
-
is actually enough to run, and there's a
screen cast that I encourage you to watch,
-
that's associated with the book chapter,
that does exactly that. All it does is run
-
the three tests, and the tests don't do
anything, so our spec prints them out in
-
yellow. Right. Yellow is not yet
implemented, just like it was for