Wednesday, October 05, 2016

RubyConf 2015 - A Guided Read of Minitest by Nate Berkopec

ese.
Oh boy.
Alright, so, my name is Nate,
I'm gonna talk into the mic while I do this, here.
And today we're gonna talk about Minitest.
I know in the program it said
a guided read, and we are gonna read code today.
This is basically a code reading talk.
But, as I was writing this talk
I realized it's really about pain.
The pain that testing makes us feel.
So we're gonna talk about pain.
You probably could have titled this talk something else.
Minitest: It Hurt Me And I Liked It.
(laughter)
My name's Name's Nate Berkopec.
I write about full-stack Ruby app performance
at Nateberkopec.com.
Performance is my usual wheelhouse.
I'm not a testing guy,
I don't usually write about testing.
Testing is sort of, the last thing
on my code skill improvement list sometimes.
But, Minitest played a huge part
in my evolution as a tester,
and, as I'm going to show you, as a Rubyist.
So that's why I wanted to come
and talk about it here to you today.
Oop, wrong way.
I'm gonna start off by telling you a story.
I started learning to program
in 2010.
I was a college senior.
Ruby was my first programming language.
I wanted to get a job at a big flashy startup,
and I thought the best way to do that
would be to become a programmer.
Turned out that was true.
When I started learning Ruby
I used Michael Hartl's Rails tutorial,
I had no computer science background.
And if you've ever looked at that,
you know Michael Hartl does a great job
of teaching you TDD from the start.
But, he uses R-spec,
and for a long time when I started
with Ruby, testing was really hard for me.
I was just getting started with Ruby,
I barely understood what I was doing
with Ruby to begin with,
and then along comes R-spec
and there's sort of like, well you gotta learn
"describe" and "it" and
all these other words that R-spec
concepts that R-spec brings to you,
and says, you have to learn those, too.
And for a while that was really frustrating to me.
And then I found Minitest.
And it was kind of like a brand new day for me.
It was like, oh, it's just Ruby,
I can just write whatever I want,
you might have heard this sort of line before
if you've ever done anything with Minitest.
I knew the API was very simple
and it was a lot easier for me
as a beginning Rubyist to write.
But...
I came upon this test
and I wanted to stub an object,
I wanted to stub an object's instance method.
And I was really frustrated,
'cause it seemed like there was no way
in Minitest to do that.
And I went into the Minitest IRC channel,
and I said, "Guys, this is dumb.
"R-spec makes it really easy to do this,
"and why won't you let me stub an instance method?"
And Mike Moore,
the maintainer of the Minitest Rails Gem,
among other things,
oops, I keep going the wrong direction, here,
said, "Well, that's 'cause we already
"have it in Ruby.
"It's called a singleton method."
I was like what?
You can do that?
And that sort of started
this long love affair with Minitest
where Minitest kept showing me parts
of Ruby that I'd never looked at,
or I'd never understood.
And so, by reading Minitest source code,
we can re-engage with Ruby.
In this way, pain,
feeling this pain of,
ugh it's really hard to do X-Y-Z,
led me to learn something new about Ruby.
Is this a talk about Minitest,
it's in the Less Code track,
so I do have to address the elephant in the room,
and that is RSpec.
I think the original
CFP, or something, for this conference,
this track specifically, said that Less Code
was like 500 lines or less.
Minitest is not that small,
but I did get into Less Code track anyway,
and I think it's 'cause we all understand
that Minitest is small in comparison
to the other options that we usually
reach for when testing
in Ruby.
But I'm not here to play internet fight,
I'm not here to talk about RSpec,
I'm here to talk about Minitest.
And to get it totally out there,
I mean, it's pretty obvious,
I came to give a talk about Minitest,
I am a Minitest guy,
so I want you to take everything I'm saying
here with that grain of salt in mind,
I don't want you to swallow everything
I'm saying here
just wholesale.
This talk is about the philosophy of Minitest
revealed through its code,
and we can't really talk about that
without talking about RSpec,
because a lot of what makes Minitest's
philosophy so interesting
is what it doesn't do.
And we can't talk about what something doesn't do
without talking about something that does it.
And in that way I think Minitest is best thought of
as a reactionary testing framework.
It's a reaction to other kinds, or other styles
of testing.
Bit of a history lesson here,
the original Minitest
was 90 lines of code.
Ryan Davis, sitting right up here in front
was nice enough to resurrect that version for me,
and it's available there
at "tinyurl.com/originalminitest,"
for an interesting read.
And if you've ever wanted to know
what being terrified feels like, it's talking about
someone's library while they're sitting in the front row.
(laughs) And Ryan has said in other
conference talks that
it was originally a replacement for test-unit.
He sat down,
was saddled with the maintainership
of test-unit, thought it was
too complicated to even understand,
and wanted to see if he could write something
that did what test-unit does,
but in less code.
And it's from that beginning that we have
Minitest, that we have today.
Before we actually read a single line of code though,
I think you can learn a lot from a project's
file structure, and its general,
where its lines of codes live,
and how many there are.
So before I dig into a library,
I think one of the greatest things you can do
is open up Cloc.
It's this little library for
counting the lines of code in a project,
it's smart enough to break things out
by file, to break things out by language,
so if you have multiple languages in a project,
it'll say, oh, well it's got ten thousand lines
of Ruby and five thousand lines of Javascript
and so on and so on.
And if we apply Cloc to Minitest, you get this
crazy output, but the gist of it is,
Minitest has 1,699 lines of documentation
in the form of its readme,
and all the non-blank comment lines.
And just 1,586 lines of Ruby code.
That's also blanks removed.
That maybe sounds like a lot, I guess,
a thousand is like, a big number.
But, let's take a look at something else,
so RSpec has four major components.
Minitest lives in one Gem.
There's a huge plugin infrastructure,
but if you Gem Minitest, there's no
Minitest dash a million other things
that get required.
RSpec has four major components
that get required when you add RSpec
to your Gem file, that is rspec-core,
expectations, mocks,
and all three of those depend
on something called rspec-support.
In total that is one thousand, five hundred,
eight hundred and, well 43 lines,
I just messed up saying that.
But it's a lot!
It's a big number, it's so big I can't even say it.
(crowd laughing)
Minitest's entire library is about as big
as RSpec's support library.
So Minitest is about one tenth the size
of RSpec in total.
If Minitest was The Mouse and The Motorcycle
by Beverly Cleary, RSpec is Ulysses,
by James Joyce.
That is a literal relative size comparison.
I'm not sure how far
that metaphor goes, but Ulysses involves
some guy getting plastered, wandering around,
and not much happens,
which sort of describes my experience
reading RSpec in preparation for this talk.
So, that's Minitest's philosophy point number one,
do less with less.
You don't go from 15,000 lines of code
to 1,500 just by cutting out
like waste, and being a better Rubyist
than everyone else.
You get it by cutting scope.
And, so when we go in and start reading Minitest,
we're gonna be looking for the things
that Minitest doesn't do.
Another good example, RSpec includes
1,639 lines of formatters,
just for printing your test results.
There is an HTML formatter, JSON formatter,
a whole bunch of other ones that I don't
know what they do.
But, Minitest spends
it's entire library doing what RSpec does
just to show you what it did.
So, moving on from that, I guess,
rspec-mocks, and this is a particular example,
I think, of a difference in priority,
between RSpec and Minitest,
is 28 times larger than Minitest's
included mocking library.
You can use any mocking library you want
with Minitest,
but the one that's included is this
minitest-mock.rb file.
And that is 28 times smaller
than what RSpec includes by default.
So, without even reading mock.rb,
or reading rspec-mocks,
I think it's fair to say that Minitest is a Statist
testing library. (crowd laughing)
That Minitest is, not in like the Stalin way,
but in the Statist mockist...
testing philosophy dichotomy.
If you don't know what I mean by that,
I think the late James Golick said it best.
"A stateist tester asserts that a method
"returns a particular value.
"A mockist tester asserts that a method triggers
"a specific set of interactions
"with the object's dependencies."
And, this is not, oop, wrong button.
So, we know
that if Minitest is gonna provide us 28 times
less mocking power and capacity than RSpec is,
mocking in Minitest will be hard.
We will experience pain with Minitest,
like I did, when using
test doubles, mocks, stubs.
And I think Minitest is trying to tell us something
with that, it's trying to say, don't do that.
If this is hard, maybe you should try something else.
Maybe you should try
code that doesn't need this, so,
that doesn't need mocking.
So, that was kind of the first instance
of, I think Minitest using pain to put us down
a particular testing and coding path.
And here's another.
The largest file in RSpec core
is configuration.rb.
It is 758 lines, again, roughly half the size
of Minitest in total.
The largest file in Minitest is minitest.rb.
446 lines.
And what I think Minitest is kinda saying here
is that configuration,
maybe this is like sort of the old Rails saying
that we've always heard, like,
convention over configuration.
I've been trying to run with this pain theme, here,
so I think it's, Minitest knows what's best for you,
and you better do it, dammit.
Configuration is not a huge component
in Minitest when we compare to reading RSpec.
Minitest is extensible,
and we're gonna see that in some of the code
that we read here in a second.
But we should not expect
a huge amount of configuration
or that sort of like...
settings and that sort of business.
Ryan has called this,
"turning testing up to 11."
I would say it means that Minitest
has a testing philosophy and it would
like you to adopt that when using it.
It's designed to teach you a certain style,
and that style is not necessarily
up for debate when using Minitest.
It's extendable, but it's not really configurable.
And I would contrast that with RSpec's
approach, which seems to suggest
that anything is possible with RSpec.
There's all these hooks,
there's all these configuration points,
ways to make RSpec do whatever you want.
And you can do that with Minitest,
but maybe it'll be a little more painful
than RSpec.
And maybe you should think twice before doing that.
So, you might be thinking, Nate,
you've talked for about fifteen minutes now,
and we haven't looked at a single line of code
and I agree.
So, let's talk about Minitest.
How does it work?
Here's a typical
extremely simple
Minitest test.
I want to call your attention to
the concepts
or big things that Minitest
is going to require you to understand
to use Minitest.
We require something, okay,
cool, like I'm a Rubyist, I know
what requiring does.
Or maybe I have some idea.
There's a class, alright?
I've used those before, I've seen
that once or twice.
It inherits from something called Minitest-test.
So that's interesting, we need to understand
what Minitest-test is
and what it's gonna give us.
'Cause as soon as we inherit from
a class, right, we're gonna get all that class's methods,
so Minitest-test could have a million methods
and then now we're working in that world, so,
who knows what's happening there.
And then, we have def,
we have a method, which is test-the-truth.
I could pick a bigger example here,
but, if you had a larger Minitest-test
you would notice that every method,
or most of the methods started
with test underscore something.
So that's interesting, probly has a meaning
that we should look for in the code.
And finally we've got this method,
which probably, 'cause we can guess,
there's an inheritance here.
Assert-equal probably comes from Minitest-test,
which takes two parameters.
So, those are the concepts
that we're gonna be looking for digging into the code.
If we run that file, just with Ruby,
the command line tool,
we get all this output.
We don't know how that output shows up.
So, let's start from the beginning.
Requiring Minitest slash autorun.
My guess would be that I should
go looking in Minitest's library
for an autorun.rb file in lib,
Minitest, autorun,
which is exactly what I find.
Slightly simplified, I cut some stuff
out of the beginning here,
but this is what that file looks like,
it just requires some other files,
which is great, we know how that works,
and calls Minitest.autorun.
Well, where's Minitest.autorun live?
It lives in Minitest.rb,
which was what we might expect
if we were looking for a class method
on Minitest.
And it looks like this.
Now, you might be thinking, Nate,
I thought you were trying to sell me
on Minitest was the simple testing library
and look at how complicated that method is!
And I think that's interesting.
I had that same initial reaction looking at this code,
and I want you to pay--
- [Voiceover] So do I. - (laughs) So does Ryan.
But, I want you to pay attention to
the shape of this method,
like if you stood here, and squinted,
and just paid attention to,
alright it's about ten lines,
it's fairly sequential, there's not,
there's one sort of long line here,
but otherwise it's pretty short.
And there's just one method here, right?
I think...
yeah, there's only one other Minitest method
that's called here.
Minitesta.run,
we've got some Ruby stuff here, like at-exit,
we're setting some variables and stuff,
but there's only Minitest.run.
That pattern is gonna have to happen a lot more
as we keep reading this library.
And I think that that pattern
is in great contrast to RSpec, which I'm going
to get to in a second.
All Minitest is doing here is installing
an at-exit hook.
If you were wondering, from the previous example,
how does Minitest know to run our tests?
It's required before we define any of our tests,
so how does Minitest know
where our tests are, and how to run them?
That's because when your program runs,
there's nothing to do,
'cause there's no code to execute,
there's no Minitest.run at the bottom
of your test file.
So, your program just exits.
And when it exits,
basically we do some other stuff,
and then call Minitest.run.
Nine tenths of this method
is defining some stuff to
give you the proper Unix exit code,
but really the meat of it here, is Minitest.run.
If we just sort of collapsed
the at-exit block here,
it would just say at-exit,
do some stuff, unless,
class variable, installed-at-exit,
then set it to true.
Clearly it just meant to prevent us
from running that method twice.
Fine, not complicated.
This is how RSpec does that.
Not that much more code,
maybe like twice as much code.
Literally just does the same thing,
installs an at-exit hook.
But, again, I want you to pay attention
to the shape.
Very different, very interesting.
One, two, three, four, five,
and I eliminated like five other methods,
that RSpec defines to
carry out this whole at-exit process.
This is interesting, you probly can't read this
down here, but the disable-autorun
class method on RSpec
just sets the autorun-disabled instance variable
to true.
That's an interesting pattern
that you don't see anywhere in Minitest.
I think it's a very interesting
expression of, this is what RSpec considers
readable, versus what Minitest considers readable.
Minitest says, you're a Rubyist,
you know what this means, it means
that we've installed something at exit.
And RSpec says, well, we need to ask,
we need to put that behind
this accessor that we've written here.
Very interesting, you're going to see that a lot.
And it's (laughs) also kind of funny,
RSpec does all this to install this at-exit hook
and then tells you not to use it,
because they want you to use the RSpec test runner,
when you type RSpec, whatever-file.rb.
There is no test runner included in Minitest.
You just use the Ruby command line interface,
although there are plenty of Minitest compatible
test runners, like the one you use in Rails.
Oh, that's the better view of that example
I just gave you, there.
Again, very interesting differences,
in opinion on what is readable
and what is not.
So I think that this means that Minitest
is trying to tell you that
Minitest thinks that you should do
the simplest thing that could possibly work.
And I want you to note the differing philosophies, here,
on simple.
RSpec, in terms of its testing interface as well,
tends to think that simple is English readable,
or looks vaguely like English.
And that's sort of the whole spec syntax
mindset.
But I think in a lot of ways, Minitest says,
well you're a Rubyist, Ruby's simple,
just read Ruby.
At the end of that
at-exit hook that we installed earlier,
you'll remember we called Minitest.run,
and that's what determines the exit code.
Let's look at Minitest.run.
Alright, so right off the bat, before we even get to,
again, look at the method shape here, very different
than a lot of what you find in RSpec.
And I'm kind of picking out some of the most
complicated stuff in Minitest, like,
this whole stack of,
Minitest goes through this whole stack of methods
before it actually runs your test,
and it's kind of the more complicated
stuff in Minitest.
There's a lot that's a lot simpler than this.
But right off the bat, before we even get
to the second line, here, check this out.
Self.run, reporter options,
where the hell are the parentheses here, Ryan?
Where'd those go?
(crowd calling out)
So let's talk about Seattle style for a little bit.
For the uninitiated,
Seattle style, I think has a lot of different
definitions, for the purposes of this talk,
I'm just going to say, it's sometimes interpreted
as don't use parentheses in method definitions.
Sometimes you go further and say don't use
parentheses except where absolutely necessary.
Which kind of leads to some interesting style choices,
it's not super popular.
David Brady gave this great example
of parentheses usage.
I'm gonna preface this by saying
this sort of code that you're looking at right now
does not exist in Minitest, this does not,
something similar that you would see
in Minitest, I'm just making this up.
We've got hash.fetch,
delete, and then puts, right?
I agree that
parentheses makes this more readable, right?
Parentheses are sort of like this tunnel vision,
you can say alright, what's the inner most
parentheses here, okay,
the foo symbol, alright,
let's go out one level, okay,
hash.fetch, okay so we're gonna
fetch the foo symbol from this hash,
alright, outside of the next set of parentheses.
Alright, array.delete,
okay we're gonna delete that thing from an array,
got it, and then we're gonna put it to the screen,
okay, fine.
But, maybe that's not the dichotomy here,
maybe that's not the
reason that we're not using parentheses.
What if that code looked like this?
This is a contrived example, so I don't think
it plays as well, but I couldn't think
of a good, actual code example for this.
What if we blew the original code out into three lines
instead, did something like,
actually described what the thing was
that we were getting out of this hash,
and then putting the side effect onto a different line.
Just an idea, I think this is a lot more readable.
I think it might be interesting.
I've honestly never considered this until
I read the Minitest source code all the way through.
It might be interesting to
take parentheses out of your code,
when you're writing it, and then see
what that makes you do.
What does your Ruby code have to look like
when not using parentheses in order to be readable?
Is that result more readable
than using parentheses at all?
An interesting exercise, I think,
I'm know I'm gonna be doing that
in the future, just to see if I like it.
So what I'm trying to say with all this is that
I think Minitest has a philosophy
that pain is good.
And we can navigate
our coding with pain.
We can use it as a kind of signal,
as a kind of,
well I feel pain here, so what does that mean,
what should I do?
Is there a way I can make this go away
without just hiding it?
I already talked about the mocks example,
I think what Minitest is saying there
is mocks are painful here,
try writing code that doesn't need the mocks,
rather than coming up with a better mocking library,
or a mocking library that makes this code easier.
Minitest also makes adding assertions very easy,
which I'm gonna talk about in a second.
When it's doing this I think it's saying,
it's trying to give you the opposite,
it's saying, well,
making new assertions are easy in Minitest,
so I am encouraging you to write your own assertions.
RSpec makes it easy,
but it makes it difficult to understand.
Seattle style can encourage
the use of local variables' intention-revealing
method names,
and, perhaps, I think, the Seattle style
tries to suggest to us, parentheses are sometimes
a band-aid on a problem
that we should fix without them.
So, I've kind of outlined how Minitest starts
running your tests,
but how does it actually know where your tests are,
how does it know how to run them?
Minitest has this class runnable,
which Minitest-test inherits from this runnable.
And basically I've omitted a ton of code
from runnable here, but,
all it does is keeps track of this class variable,
runnables, simple enough.
And then you see this method, class method
inherited, which just adds something to the runnables
array, and then calls super.
I'd never seen this before,
so I had to go look it up.
I first searched through the project
and said well, where is inherited defined?
It's not in the project.
Okay, well it must be something in Ruby.
Inherited is a callback that you get for free,
I think it's on class, it might be on module.
And it's just called any time that
you get a new sub-class defined.
This is just straight from the Ruby documentation,
as an example,
if we sub-class foo,
this method will get called with
the new sub-class
as an argument.
So literally all that's gonna happen here,
is my test
will get passed as an argument
to Minitest runnable inherited,
which will add it to the runnables array.
Eventually, Minitest looks at
the Minitest runnables array
for all of the test classes that it knows about
and just runs through them one after another.
I'm not gonna talk about test discovery
in RSpec, I ran out of time.
Standard Lib is used very heavily
in the Minitest source code.
We see things like OptionParser, Thread,
Mutex, StringIO, Tempfile.
And in something I can't say I've
ever really seen before,
Minitest will use your system Diff tool
when printing
the difference between two large objects,
like if you assert-equal, some huge array,
some other huge array, and like one thing
in that huge array is different,
Minitest will use Diff
to figure out which thing was different.
RSpec implements their own version.
RSpec implements that in Ruby.
And Minitest instead says,
well I'm just gonna use what's provided.
Interestingly, a lot of RSpec
re-implements a lot of Core and Standard Lib classes.
They have their own version of Set,
they have their own version of Flat Map.
They use their own version of Thread Local Variables,
and they even have their own Rand implementation.
Some of these I understand,
these two I think have Ruby version issues,
not sure about Thread Local Variables,
but threading's a total, you know,
put your hands up thing in Ruby, so,
maybe there's a good reason for that.
Rand, I don't know.
I was looking at,
I don't think I put the example in here,
but...
RSpec uses its own algorithm for
setting the random seed in your test sweep.
There was some code comment that this
implementation that they use is just like,
somehow better,
that's it, it's better somehow.
I don't know.
So I think Minitest has a philosophy
to use what is given to you.
To use what is given to you by the system,
to use what is given to you by Ruby itself.
That's an interesting philosophy that I think is
something that we're really not used to,
as Ruby developers, and I think
it's to our detriment.
A lot of us have a kitchen-sink philosophy
when it comes to our Gem files.
If there's some library that will save
me ten minutes of effort,
that I can put on my Gem file, then I will do it.
Devise I think is kind of the worst
offender here, not because Devise is a bad library,
but because we use Devise to do simple,
user passname authentication that we could
just write ourselves or use Rails facilities
for that, and be done in ten lines of code.
Anyway, so I think that's an interesting philosophy
point from, that we can take out of reading Minitest.
So how do we actually define...
am I saying test, here?
Yeah, so how do we define a test?
How do we define the...
if we say a class file is sort of
a collection of many different
things we want to test in Minitest,
how do we define one of those?
This is in, where is this,
Minitest-test.rb, and it gets called
in Minitest.run, which was on
a previous slide.
We ask a Minitest-test sub-class
for its runnable methods,
we say, dear sub-class,
please tell me which methods you would
like me to run.
On Minitest-test that...
is really, this is all just ordering them.
It's just this methods matching some regex,
which is just matching test underscore.
The methods matching method is literally
just a one line error that basically just
calls Grep on the collection of methods
and brings them back as an array of strings.
So really it's just, okay, a test in Minitest
is just a method that starts with test underscore.
Also interesting I think that this
is not configurable.
There's no, you can monkey patch it of course,
but it's not like some, there's not some hook
provided that let's you change out
that regular expression.
Very different in RSpec.
RSpec has, from what I can tell,
13 different ways to define a test,
or an example.
Granted, they do a little different things
in RSpec, but there are three that do exactly
the same thing.
Example in specify,
there's ways to define a test while also
focusing and skipping at the same time.
And there's also a pending example,
pending apparently has some other semantic
meaning in RSpec.
And this method here is also provided for you in RSpec,
so you can do this yourself, you can say, well,
I want the smiley face emoji
to define a test method in my RSpec tests,
so I can do that.
Minitest says, that doesn't matter, just do it this way,
it's fine.
There's maybe some reasons for this,
but if you don't think that
Exit being a way to skip a test because of,
I don't know, I think
J-unit or X-unit used to do that.
If that doesn't matter to you,
then maybe RSpec is providing
other things that don't matter to you.
This is RSpec implementation of define
example method.
Does this thing called item potently define
singleton method.
I'm not a computer scientist, so,
like I don't really know what this word means.
I know what a singleton method is,
but I don't know why they didn't just do that,
instead of calling this other method that I guess
does it for me.
There's some metadata,
I don't know what metadata is.
And, well this I understand, examples.
Yup, there's some examples here,
we're adding an example there,
that's cool.
Very different approach.
So, what I think Minitest
is espousing here, is to reduce our API services,
let the user define their own aliases if they want,
and make it simple to do so.
And I think that kind of goes along with
limiting configurability.
I'm kind of running out of time, here,
I'm okay.
Yeah, just talk faster, Nate.
Let's talk about assertions really quick.
This is an example of assertion in Minitest.
The structure, as you can see,
is really simple.
If you look at Minitest-assertions.rb,
they literally all look like this,
they're all like three lines.
It defines a message that we print if
the assertion fails, and then makes an assertion
using the assert method.
So, Minitest doesn't really provide
any facility for writing your own
assertions, it just says do this
on your own tests.
Just open up Minitest-test or open up
your test class, and write something
that looks like this.
There's no specific facility,
like what RSpec provides,
to define a new
assertion or matcher, as RSpec calls them.
This is what assert looks like.
Not any more complicated.
We increment our assertions count by one,
and then run the test,
the thing that we wanna test,
and if that returns true,
we just keep moving, and if it doesn't return true,
we're going to raise this special kind of exception,
which Minitest handles in its own way,
I don't have time to get into that, unfortunately.
And you might have noticed here that
message is a proc, which is sort of interesting.
Maybe a little more complicated.
Originally it was just a string,
but if you think about it,
if we're calculating the error message
every time we run the test, eventually
that's gonna get very expensive.
So by putting it into a proc,
we can only calculate an error message
when we actually need it.
You don't have to do that
when writing your own assertions,
because if it's just a string or whatever,
Minitest will just print it.
If you don't care about that or
if it's not a big problem for you.
And the way that these get included
into your test, these assertions,
is we just include the assertions module
in Minitest-test.
We've all done that a hundred times,
no big deal.
RSpec, I honestly have no idea
how they do this.
The expectations Gem itself
is like 3,000 lines, and then there's
shoulda-matchers which is another 6,000 lines.
I really honestly, I've sat down and read RSpec
to try to give it an honest comparison in this talk.
This part of RSpec I could not understand.
This is the facility that RSpec provides
for defining your own matchers.
It's interesting 'cause this is very
characteristic of RSpec.
It reads quite well,
if you like read it out loud in English.
But, these are all new concepts,
which you have to go learn about
and understand how they work.
This define method.
Where this match method comes from,
I have no idea.
And what these block arguments are actually
doing, I think are all questions that you have to go
ask RSpec about.
Whereas
this, use a module,
and call this method that raises a Minitest-assertion,
that's pretty simple to me, I guess.
What I think Minitest is saying is that abstraction
is the enemy.
The pain here is that you will have to
write your own abstractions sometimes.
I think that's okay, I think that makes us
better Rubyists and I think
learning to write abstractions well
is one of the greatest skills
that we can learn as programmers.
And God forbid that you learn some
Ruby along the way,
rather than learning how to better use
RSpec.
To extend and use Minitest
in a greater capacity, you usually
just have to write more Ruby.
With RSpec you have to learn to use RSpec better.
Because it's a DSL that you have to interact with.
So I think, I hope this has shown, through a little bit
of code, though not as much as I would like,
that Minitest in a lot of ways is a philosophy about pain.
At least, it is to me.
And maybe you've had pain with many tests in the past,
maybe if you think back,
and on a time that you've had to struggle with Minitest,
or any time that you've struggled with Ruby, really,
did you just give up, and go throw Devise in your Gem file?
Or go throw
whatever Gem you thought you needed
to get this done without you having, God forbid,
having to write any more code.
Or did you listen to that pain?
And did you think about what that pain
was trying to tell you?
I think in programming,
like life, what doesn't kill us
makes us just that little bit stronger.
Thank you, that has been my talk.
(applause)
Please go read Minitest, if you haven't,
it's really easy to read.
And...
go test, more. (laughs)
And my slides are available at that URL.
I guess, I have five minutes, so if anyone
has any questions?
No? Cool.
Thank you for listening to me yap
for 45 minutes, appreciate it.