Transcript

This lightning talk was recorded at RailsConf 2022 in Portland, Oregon. It shows off our new mocking library for Ruby, Mocktail.

It’s a bit of a whirlwind, so don’t feel bad if anything goes over your head! Please feel free to take your time perusing the library’s expansive README.

If you’re looking for another talk on this topic, consider watching the companion talk, Please don’t mock me. It’s a much longer talk, and so it takes the time needed to discuss some of the patterns and pitfalls to keep in mind when using test doubles in your tests.

00:00
- Okay, so the title of this presentation is
00:01
"Please Mock Me."
00:02
So hello everyone, Portland.
00:04
Lovely weather today.
00:05
A year and a half ago,
00:06
I moved to Florida.
00:08
So today, you know, the weather's really
00:09
selling me on Orlando.
00:11
I'm talking here about test doubles,
00:13
not our company Test Double,
00:15
but rather the concept of test double.
00:17
It's just a highbrow way to say a mock object.
00:20
So we're gonna talk about that today.
00:21
Now Rubyists have lots of options for mocking.
00:24
You have tools like Mocha, rspec-mocks,
00:26
rr, flexmock,
00:27
minitest has a built in mock thing.
00:30
There's probably others, but the WiFi's not working.
00:33
I've got a secret,
00:34
which is I don't like any of them.
00:37
And so, you know, my approach to open source
00:39
is actually very unlike what Eileen presented today,
00:41
and we're gonna talk about that through this pop quiz.
00:43
So get ready.
00:45
Here's the question:
00:46
what should you do when you don't like an open source thing?
00:48
And you have two options:
00:49
Option A, help improve the thing,
00:51
or Option B, create a brand new thing.
00:53
B!
00:55
- Yeah, my style is I create a brand new thing, ignorantly.
00:58
So that's how this gem happened
00:59
and you can gem install mocktail.
01:02
So let's say we have a class called "CreatesSlides"
01:04
and it's got a method called "create",
01:05
and you pass in a topic
01:07
and then it it literally opens the Keynote app.
01:08
And so this is disruptive to call from a test.
01:12
So you might say, "Hey, I wanna fake this."
01:14
Now we don't have time today to answer,
01:15
"Should you fake this?"
01:16
That's the companion talk called "Please Don't Mock Me"
01:19
and that's a 50 minute one,
01:20
so there's more to talk about there.
01:22
So let's say I wanna fake this.
01:24
And so, alright.
01:25
So I got the class there.
01:26
All I say is make me a Mocktail of "CreatesSlides"
01:30
and then it returns it.
01:31
So that's a fake thing,
01:32
and then I can call all of the methods on it
01:35
and call it with create("Mocking").
01:36
It just returns nil by default.
01:39
So you might say, "Oh, I wanna stub this,"
01:40
and stub just means to like kind of configure a response
01:43
for a mocked method.
01:44
So here, I've got this DSL method "stubs"
01:47
and I call the method just like I expect the subject
01:49
under test to call it,
01:50
and then I chain with "with".
01:53
In that block there,
01:53
I just return whatever I wanted to return.
01:55
So, I've made the stub and I called this a demonstration.
01:59
So it's very greppable, like the subject and the test
02:02
both usually have the exact same indication,
02:03
so it's really clear and not confusing.
02:05
And then this, when the stubbing is satisfied
02:08
and called the same way, it's gonna return that array.
02:11
Okay, so it just looks like this.
02:12
I call "slides.create("Mocking")",
02:13
it returns the array.
02:14
I call it with "Not Mocking" and it just returns nil
02:17
'cause the subbing wasn't satisfied.
02:18
It's a super duper robust API.
02:20
It can do everything that you might ever want to do,
02:22
especially stuff you shouldn't.
02:23
So like if I wanna ensure that a string is passed
02:26
and I don't wanna use a type system,
02:28
I could just write this test,
02:29
and I say, "stubs{slides.create()}"
02:31
and then I can take a block parameter,
02:33
which is like a little matcher DSL,
02:35
and I say "m.is_a(String)",
02:37
and only when that matcher is matched,
02:38
then return some slides.
02:40
And so I can call it then with "Mocking", you know,
02:43
"Literally any string",
02:44
or like a number here and then that's not satisfied,
02:46
so that will return nil.
02:48
And so there's lots of built-in matchers,
02:50
as well as a custom matcher API that you can play with.
02:53
So the README has everything you want about stubbing.
02:56
You know, it's good because the itty bitty scroll bar
02:58
so it's like very long.
02:59
(audience laughs)
03:00
So let's say you want to verify
03:02
that a certain thing was called.
03:03
So I say "slides.create("Neat")".
03:05
I call that.
03:06
I wanna make sure it's called,
03:07
I can just say "verify" that "slides.create"
03:09
was called with "Neat".
03:10
And then it just returns nil, it's passed.
03:13
And so if I say,
03:14
"Hey, verify that it was also called with "Cool",
03:15
that's gonna blow up 'cause it didn't happen.
03:17
So you got this really nice error message that says,
03:18
"Hey, expected to be called with, you know,
03:20
"create" with "Cool".
03:22
It was actually called differently one time with "Neat",
03:24
and it prints it out so you can kind of have a hope
03:26
of figuring it out.
03:27
Now it's a symmetrical API with stubs.
03:28
They totally match each other and are completely compatible.
03:31
And so I can say like, you know,
03:32
if I call it a couple times here,
03:34
I can verify just the same sort of thing, you know.
03:36
Make sure it's called with a string and that'll pass.
03:39
But if I say, like I've got additional cool stuff,
03:41
like if I said, "Make sure it's called exactly one time,"
03:43
and that'll blow up
03:44
'cause we just called it twice at the top.
03:46
So it'll say, "Cool messages here,
03:47
"expected it to be called with one time,
03:50
"was actually called this way two times."
03:52
Very instructive because mocking is confusing.
03:55
So if you are confused,
03:57
I've got a method called "explain".
03:59
You just pass it any mock
04:00
and it'll return a double explanation.
04:02
And so you can print out the message about it.
04:04
It'll like give you a little text adventure saying:
04:07
"Here, you know, this is a fake thing.
04:08
"Here's all the mocked messages.
04:11
"Here's all the stubbings
04:11
"and here's all the actual calls."
04:13
And then there's a reference object
04:14
which returns a double data,
04:16
which has all of this introspective data that you can use.
04:18
And so metaprogramming is really scary,
04:21
but it's important to have good introspection tools.
04:24
And so you can answer all kinds of questions.
04:26
Like for example, how many calls were there?
04:28
There were three.
04:29
How many times were the stubbing satisfied?
04:30
There were two.
04:31
So is that all?
04:32
No, there's other stuff too.
04:33
This supports a TDD Workflow.
04:35
And so there's a cool method missing thing override
04:37
that I did.
04:38
So like, if you wanna add a method called "destroy",
04:39
you just call it and then it'll print out here saying,
04:41
"Hey do you want define that method?
04:43
"Here's a little copy paste stub."
04:45
And so you can just kinda paste it right in.
04:47
So if you're in a creative mode, it makes it go fast.
04:49
And then for dependency injection,
04:51
you can just literally call
04:54
another method called "Mocktail.of_next"
04:56
and then pass it the class.
04:58
And what that's gonna do is when I call "talk"
05:00
it stubs the new method on the class.
05:03
And now these two things are gonna
05:04
be the exact same mock instance.
05:06
So I don't have to make a goofy dependency injection API.
05:08
It just works, it's really neat.
05:09
So if you're mildly interested in this,
05:11
you can find us on GitHub under testdouble/mocktail.
05:15
It's a Portland friendly gem
05:16
because it's only been downloaded 5,000 times.
05:19
And so it's still very hip
05:22
and I hope you all go and check it out.
05:23
So thank you everyone for your time.
05:25
Appreciate it.
05:26
(audience applauds)

Justin Searls

Hash An icon of a hash sign Code Name
Agent 002
Location An icon of a map marker Location
Columbus, OH