00:00:00.240
this is a talk that I created back when
00:00:02.080
I was at my previous job at gusto in
00:00:03.919
like 2019 that I've given two or three
00:00:06.359
times since then in some format or
00:00:07.840
another um and if you're wondering why
00:00:09.960
is an infrastructure engineer talking to
00:00:11.480
us about arpec stuff um my background is
00:00:14.000
actually all in rails so I started out
00:00:16.720
doing um backend rails engineering kind
00:00:20.320
of moves to full stack rails and react
00:00:23.240
um and worked at a few different places
00:00:25.000
and then sort of switch into the
00:00:26.320
infrastructure devop space back in
00:00:27.960
November of 2020 um but still have kind
00:00:30.599
of worked in sort of a role that Bridges
00:00:32.480
the gap between uh like pure
00:00:34.360
infrastructure work and the engineers
00:00:36.120
working on the application so I still
00:00:37.680
kind of hop in from time to time to do
00:00:39.239
different things um and before I left
00:00:41.480
Gusto I was leading uh what we called
00:00:43.600
the testing Guild we're trying to
00:00:45.520
improve sort of testing practices around
00:00:47.239
and that was uh where I last gave this
00:00:50.000
talk um so to talk about testing
00:00:54.239
generally uh real quick there's
00:00:55.879
difference right between a test and a
00:00:57.840
test weite um and I'll pause here for a
00:01:00.600
second too and say this talk is very
00:01:02.440
participatory um we're going to get to a
00:01:04.280
lot of opinions that I have that I've
00:01:05.560
built up over the years around testing
00:01:07.320
so like I probably won't see a question
00:01:09.159
if you drop it in the chat but if you
00:01:10.680
want to raise your hand um I'll try to
00:01:12.720
be looking over and let people hop in at
00:01:14.479
different points
00:01:15.840
here uh so what makes a good test versus
00:01:19.040
a test Suite test should be readable and
00:01:22.079
easy to follow right you can tell what's
00:01:23.759
being tested easily and you don't have
00:01:25.840
to hop around to a bunch of different
00:01:27.320
places to find all the different parts
00:01:28.840
of your test
00:01:30.360
ideally they're small and you have one
00:01:32.320
assertion in there um I will say that
00:01:34.200
may not hold for end to end test or
00:01:35.840
feature tests where you're clicking
00:01:36.960
around but especially for unit tests you
00:01:38.840
want them to be small and concise they
00:01:41.200
should be accurate which means they
00:01:43.000
actually portray the behavior of the
00:01:44.520
code
00:01:45.399
correctly and it should be tested on the
00:01:47.799
correct level right so you're testing
00:01:49.719
what is actually under test and nothing
00:01:51.479
more again with feature test sort of
00:01:53.560
aside from that test Suites on the other
00:01:56.600
hand right now is the group your test
00:01:57.960
should be thorough so they should cover
00:01:59.840
all pass in your code in there any if
00:02:01.759
statement you have any tary operator you
00:02:04.000
have uh should all be covered in some
00:02:06.640
way in your test uh they should be
00:02:08.920
uncluttered and by that I mean no
00:02:11.039
unnecessary test setup right if you only
00:02:13.360
need two users and a test to test
00:02:15.599
something you shouldn't create five of
00:02:17.360
them um that makes it harder to
00:02:20.480
follow ideally your test rats are also
00:02:23.239
consistent um I'm sure people have
00:02:25.519
hopped into larger rails projects uh
00:02:28.280
where like in one
00:02:30.519
uh part of the code tests are written in
00:02:32.400
one way and another part they're written
00:02:33.599
in a totally different way and you kind
00:02:34.920
of have to like mentally keep all that
00:02:36.480
together so um I really try to on a team
00:02:39.400
sort of find a consistent pattern for
00:02:41.239
writing codes so that you just don't
00:02:43.480
have that mental overhead anytime you
00:02:45.040
hop into a
00:02:46.400
file uh and deterministic this is my
00:02:49.480
favorite one on here uh people like to
00:02:51.640
call test flaky which kind of implies
00:02:53.519
that like oh there's nothing we can do
00:02:55.000
about it they're just going to flake
00:02:56.080
some sometimes really they're
00:02:58.159
non-deterministic right so your test
00:03:00.200
should always pass or should always fail
00:03:02.680
and you shouldn't have any weird order
00:03:03.959
dependencies a their
00:03:06.480
thing I like to follow the Three A's uh
00:03:09.760
for testing if you haven't heard them
00:03:11.319
before so uh they are AR range which is
00:03:14.360
set up all the objects that need to be
00:03:15.959
tested along with any other necessary
00:03:18.040
setup an act so actually do the thing
00:03:21.319
you're trying to test and then assert
00:03:24.200
right so make claims about the object
00:03:26.159
that you're testing or any of its
00:03:27.720
collaborators on there um I tend to find
00:03:30.640
that if you follow this consistently
00:03:33.360
it's a lot easier to actually figure out
00:03:35.080
what's going on in your test um it can
00:03:37.040
be difficult when people start arranging
00:03:39.120
stuff and then acting and then maybe
00:03:40.280
arrange more thing and act more thing
00:03:41.599
they sort of doing a lot in test to be
00:03:43.080
able to actually follow what's going on
00:03:45.239
um and I'm sure since we're talking arpe
00:03:47.000
here uh the uh expect to receive always
00:03:51.799
gets very confusing especially for new
00:03:53.599
developers because you call it before
00:03:55.560
you actually act on a thing uh so you're
00:03:57.920
sort of flipping the order of a certain
00:03:59.959
act which I don't
00:04:01.280
love so some disclaimers about the rest
00:04:03.959
of this talk um all rules have
00:04:06.640
exceptions which is why I call these
00:04:08.200
best practices and not
00:04:10.040
rules uh not all the things I'm going to
00:04:12.599
talk about that I think you should do in
00:04:13.720
testing are widely practices at all the
00:04:15.319
places that I've worked and some of them
00:04:18.320
are generally accepted I would say some
00:04:20.000
of them are really like based on my
00:04:21.880
experience here uh so again feel free to
00:04:25.600
hop in if you want to disagree with me I
00:04:27.360
love being disagreed with on parts of
00:04:29.000
this talk
00:04:30.280
um the first one here is don't leave
00:04:32.759
important code paths untested right if
00:04:35.320
your method is two different if
00:04:36.919
statements having one test that only
00:04:38.680
goes through one path is not going to
00:04:39.919
help you catch any bugs there right it's
00:04:41.960
also going to make change for your code
00:04:44.800
uh harder later because you have some
00:04:47.199
existing functionality that's not
00:04:48.720
actually covered under your test so you
00:04:50.199
don't know if you're going to introduce
00:04:51.440
a
00:04:53.280
regression you should always test s code
00:04:55.440
past right your tests are documentation
00:04:57.440
in the end which is sort of one of the
00:04:58.720
things I like to Hammer home here um if
00:05:02.120
you are testing all your code pads and
00:05:04.120
there you are leaving human readable
00:05:05.919
documentation for what that code does
00:05:08.280
and what it should do in every case that
00:05:10.000
you have
00:05:11.759
there uh don't be so General that you
00:05:14.800
aren't testing the real Behavior your
00:05:16.479
code so I see this a lot where people
00:05:19.000
will say like expect this thing to
00:05:22.120
change some model. count by negative one
00:05:26.319
right uh which will pass if any instance
00:05:28.840
of that model is deleted which is
00:05:30.360
probably not the thing that you're
00:05:31.280
trying to test which is to say hey I ran
00:05:33.240
this method and it deleted this user
00:05:35.560
specifically right so instead you want
00:05:38.440
to be specific enough to actually cover
00:05:40.039
the use case that you're testing on here
00:05:41.960
so doing something like delete it then
00:05:43.880
expect that if you reload that instance
00:05:45.680
from active record you get the active
00:05:47.120
record record not found error right um
00:05:50.520
so it's important if you say like this
00:05:53.199
test is meant to delete this user that
00:05:54.720
you are actually doing
00:05:57.600
that uh this one tends to be a little
00:06:00.280
controversial um I hate shared examples
00:06:02.919
and shared context um I have seen in all
00:06:07.039
the real shops I've worked at that they
00:06:08.560
tend to be uh like start out small and
00:06:11.759
sort of helpful and then quickly become
00:06:14.080
very bloated I have seen shared contacts
00:06:16.319
referencing shared contacts referencing
00:06:17.800
shared contracts across like multiple
00:06:19.680
files there to the point where you have
00:06:21.720
no idea where these objects are coming
00:06:23.280
from uh that are being set up and to be
00:06:25.720
honest I just don't want to go to a
00:06:27.240
different file uh when I'm looking at
00:06:29.039
tests like I want to be able to just
00:06:31.840
have everything there in one
00:06:33.919
test um I'm sure everybody's heard the
00:06:36.479
dry acronym right don't repeat yourself
00:06:39.240
uh when it comes to test files
00:06:41.479
specifically I like to do the opposite
00:06:44.360
which is wet which is widely explicit
00:06:46.440
test right sometimes when you extract
00:06:49.759
these things out like a shared example
00:06:51.960
um to have you know less lines of code
00:06:53.840
you can drop in for all these different
00:06:55.560
use cases you're actually hiding the
00:06:57.639
fact that you might be able to like
00:06:59.879
extract some functionality there that
00:07:01.639
would allow it so you don't have to
00:07:03.000
repeat yourself so by making it really
00:07:04.919
easy and dry you're potentially like
00:07:07.759
hiding uh an extraction that could be
00:07:10.599
that you might have noticed otherwise
00:07:12.759
right um so I personally shy away from
00:07:15.919
these uh at my first team at Gusto I
00:07:17.919
just banned people from using them and
00:07:19.840
refus to approve any PR on our team that
00:07:22.560
had them that's how strongly I feel
00:07:24.560
about
00:07:26.080
that uh don't ever assert absolutely
00:07:29.560
database counts um if a method adds a
00:07:33.000
record to the database you don't
00:07:34.879
actually care right like how many things
00:07:37.560
were there before that right the thing
00:07:39.560
you're actually testing is that
00:07:41.160
something got inserted to the database
00:07:42.919
has nothing to do with where there's
00:07:44.120
zero things in there before five things
00:07:45.840
in there before right so I see a lot of
00:07:48.599
more Junior developers do something like
00:07:50.840
assert user. count is zero and then do
00:07:54.039
something and then assert user. count is
00:07:55.960
one right which can make the test very
00:07:58.720
brittle also because if somebody puts a
00:08:01.159
let bank statement at the top of the
00:08:03.000
file for a different test let's say that
00:08:05.360
includes a user now this existing test
00:08:08.360
is going to fail when it shouldn't have
00:08:10.400
really right has nothing to do with that
00:08:12.280
so um the change match is the best way
00:08:15.400
to get around this right you can expect
00:08:17.159
something to change by one and then you
00:08:19.879
don't have to care about how many things
00:08:21.159
were there
00:08:26.039
beforehand uh it's also can be pretty
00:08:28.319
brittle to really explicit date and
00:08:31.279
times uh tests can flake I seem to
00:08:34.159
happen a lot due to like millisecond
00:08:36.680
failures right where you're asserting
00:08:38.360
some time against another time there and
00:08:40.959
to be honest you probably don't care
00:08:43.159
about being that specific down to the
00:08:44.920
few like milliseconds or nanc right so
00:08:48.080
one of the things I found in my time
00:08:49.480
testing uh is that you can call on a
00:08:52.040
Time object this uh do change and then
00:08:54.560
NX zero and that will basically like set
00:08:57.399
all the msus everything down to zero so
00:08:59.680
you're never worried about that level of
00:09:02.160
granularity or if you only care about
00:09:04.839
like a date part of it and not the time
00:09:07.480
part of it um only actually assert about
00:09:10.760
the part that you care about like parse
00:09:12.320
it into a string and just assert against
00:09:14.279
the year month and day or something like
00:09:18.200
that uh this one I see bite people all
00:09:22.480
the time uh especially in controller
00:09:24.839
tests but don't assume there's going to
00:09:27.640
be an order to results if the underlying
00:09:29.920
query that gets that data for you has no
00:09:32.560
order on it um as part of my testing
00:09:35.600
Guild work at Gusto I put out a survey
00:09:38.360
at one point to all the engineers that
00:09:40.000
ask some questions about testing and one
00:09:42.640
of them was uh if you don't assert an
00:09:45.240
order for your database query will you
00:09:46.920
get it back in ID order and the answer
00:09:50.279
is no most of the time yes but you're
00:09:53.680
not guaranteed that ever so making that
00:09:56.000
assumption uh is going to lead to flakes
00:09:58.519
at some point
00:09:59.880
uh so a lot of times I'll see when
00:10:01.200
people are testing against like a
00:10:02.839
controller and they have an endpoint
00:10:04.040
that's going to return an array of
00:10:05.360
things they'll make an assertion on the
00:10:07.760
first thing and just say you know
00:10:09.079
whatever my response. first is should be
00:10:11.040
this my response. last should be this
00:10:13.200
and then eventually that's going to
00:10:14.279
flate because at some point those things
00:10:15.720
are to come back in the opposite order
00:10:18.000
here uh so do use find the array method
00:10:22.079
find to pick out the element based on
00:10:23.959
some identifier right so if you're
00:10:26.480
getting two things back from an in point
00:10:28.440
you can say find me the one that has id1
00:10:30.600
and then I'll go assert some other stuff
00:10:31.959
against it find me the one that has id2
00:10:34.160
I can go assert some other stuff against
00:10:37.279
it uh related to that don't assert order
00:10:41.200
if order doesn't matter so if you use uh
00:10:45.320
the match uh identifier for arpec and
00:10:49.000
you say you want this thing to match
00:10:50.480
another thing match is always going to
00:10:52.600
assert that those things are in the same
00:10:54.600
order here uh Alon I see you have your
00:10:57.040
hand
00:10:58.040
raised yeah I'm I'm sorry do you do you
00:11:01.120
know why um you don't get the IDS back
00:11:05.320
is it's like a like a Computing thing
00:11:08.880
like a excuse me for not explaining it
00:11:11.680
really well but um like their request
00:11:15.279
doesn't return to like postris or
00:11:17.680
whatever database you're using doesn't
00:11:19.240
like takes a little bit longer to get
00:11:20.800
certain records back and so it's not
00:11:23.560
like inter leaving the results I don't
00:11:25.680
actually know yeah in that way but yeah
00:11:29.399
so it's a it's a database slash like
00:11:31.760
active record thing I would say so so
00:11:33.480
the database is never going to guarantee
00:11:35.519
you an order unless you specifically
00:11:37.320
have an order by Clause tacked on to the
00:11:39.600
end of your SQL query an active record
00:11:42.079
will not add an order by Clause unless
00:11:44.160
you specifically put like a do order ID
00:11:47.320
ascending or something like that so like
00:11:49.200
if you aren't adding that order by
00:11:51.800
Clause uh with active record it's never
00:11:54.320
going to send one to the database and
00:11:55.720
then the database won't guarantee any
00:11:57.560
order coming back to you
00:12:02.600
does that make sense but it does it does
00:12:05.680
most of the time yeah now why it does
00:12:09.600
that most of the time I don't know like
00:12:12.880
I don't know that's okay weird database
00:12:15.720
Quirk I would say is that like as far as
00:12:17.880
I know you're not guaranteed any order
00:12:19.920
now I would say they just happen to with
00:12:21.760
active record come back in ID order most
00:12:23.720
of the time but again like there is no
00:12:26.519
guarantee for that uh Rose you got your
00:12:29.399
hand up yes I I recently learned more
00:12:32.920
about this than I thought I would ever
00:12:34.720
need to know um the query planner inside
00:12:39.120
a database I always imagine like the
00:12:41.680
query is a very simple thing where it
00:12:43.720
just like goes through the index and
00:12:46.120
grabs stuff and that's very repeatable
00:12:50.480
but the query planner is actually like
00:12:53.440
rather complex code all on its own that
00:12:56.199
is optimizing to make sure that it is
00:12:58.440
reading the data in the most optimal way
00:13:01.880
possible so the the database is actually
00:13:05.639
going based on a whole bunch of factors
00:13:08.720
that I don't fully understand what is
00:13:11.480
the optimal quickest way to get this
00:13:13.839
data for you which is why it might pull
00:13:16.519
it in different orders at different
00:13:18.079
times unless you tell
00:13:21.720
it yeah uh I could have a whole other
00:13:25.040
talk on database stuff but that's right
00:13:27.000
there and also like what the query plan
00:13:29.000
does it's based on statistics that it
00:13:30.720
calculates the time it's based on data
00:13:32.639
volume it could change based on adding
00:13:35.240
like a bunch of rows very suddenly to a
00:13:36.880
table that it could choose it have to do
00:13:38.320
something different um so that's that's
00:13:41.399
a whole other thing but like the I don't
00:13:43.360
know the tldr to takeway for this one is
00:13:45.639
like if you care about the order specify
00:13:48.920
the order on your query if you don't
00:13:51.120
care about the order don't specify it
00:13:54.040
but definitely don't get in the mix
00:13:55.399
situation where you care about it but
00:13:56.880
you did not actually specify it because
00:13:58.480
that's like guaranteed test flakiness
00:14:03.240
there cool thank you yeah thanks for
00:14:06.120
hopping in um okay yeah so still on that
00:14:09.560
topic of order match is going to assert
00:14:13.000
order every time so if you say you have
00:14:15.079
something match an array it's not only
00:14:17.120
going to care that the things you want
00:14:19.240
are in that array but that they're
00:14:20.360
specifically in that order um if you're
00:14:22.800
underlying query doesn't specify an
00:14:24.399
order again that's going to flake
00:14:25.920
sometimes so you can use match array or
00:14:28.320
contain exactly which will just assert
00:14:31.040
the membership of that array but not the
00:14:33.360
order of the elements in it uh so if you
00:14:35.560
truly don't care about the order of
00:14:36.800
something returned these are good ones
00:14:38.240
to use they will make it so your test
00:14:39.800
don't
00:14:41.880
flick uh this another one I've seen a
00:14:44.199
lot so Faker I'm pretty sure we've all
00:14:46.079
used to generate uh some data for our
00:14:49.120
tests for things we don't care about
00:14:50.600
right you often have Factory bot
00:14:52.480
factories that are using Faker under the
00:14:54.120
hood uh so you don't have to go set it
00:14:56.560
against uh all the time but there are
00:15:00.240
two problems I've seen with Faker here
00:15:02.120
so like Faker is really in the end if
00:15:04.600
you go and look at the code in GitHub
00:15:06.199
all it's doing is for something like
00:15:08.079
first name it's just got a list of a
00:15:09.880
bunch of first names and it's just
00:15:11.320
picking one at random out of there uh so
00:15:15.199
it's really important that if the data
00:15:17.360
that you're testing is important to you
00:15:19.560
like if you're assarting let's say that
00:15:21.759
the uh you have three users and you have
00:15:24.000
some method that's going to return them
00:15:25.639
by their last name in alphabetical order
00:15:29.319
right you need to be setting
00:15:30.759
specifically that last name for each of
00:15:32.800
those users and not letting Faker choose
00:15:34.560
it for you because one if you let it
00:15:37.800
choose it for you you don't know what
00:15:39.199
it's going to be that time right so
00:15:40.920
sometimes it might be I don't know like
00:15:44.000
Smith and sometimes it might be Anderson
00:15:46.759
and that's going to drastically change
00:15:48.120
the order of things returning your test
00:15:49.639
if you care about that um I've also see
00:15:52.720
it caused collisions where we had some
00:15:54.920
test flake because we're asserting uh an
00:15:57.360
order of like users returned by email
00:16:00.160
and uh sometimes the email got chosen
00:16:02.600
was actually like the same one as we had
00:16:04.360
set in another test because it is just
00:16:06.560
picking something at random so the
00:16:08.600
important thing is like if you care
00:16:10.040
about that data and you're asserting
00:16:11.319
against it in your test you need to not
00:16:12.800
let Faker choose it for
00:16:15.680
you um also setting environment
00:16:18.920
variables explicitly so when you're
00:16:21.839
running your full test Suite right
00:16:23.560
things are going tests are going to be
00:16:24.839
run in a random order assuming you have
00:16:27.160
the random seed set with our which you
00:16:29.000
definitely should uh so the best way to
00:16:32.680
get around uh having environment
00:16:34.440
variable collisions where it gets set in
00:16:36.160
one test and then a different test
00:16:37.480
expected to be somewhere else is to
00:16:39.040
always set them in round blocks so you
00:16:41.199
can grab the original value the
00:16:42.759
environment variable uh set it to the
00:16:44.920
thing you need it run your test and then
00:16:46.920
set it back to the original value so
00:16:48.560
you're basically not uh leaking state
00:16:51.279
right between one test and another sense
00:16:53.519
environment variables are Global to the
00:16:56.199
entire test context
00:17:00.160
uh this one I have seen people do if you
00:17:03.800
are testing a private method using send
00:17:06.439
this is a huge huge smell that you're
00:17:09.039
one violating the single responsibility
00:17:11.000
principle for your class and two have a
00:17:13.280
major opportunity to refactor your code
00:17:15.240
here right like if you find yourself
00:17:17.079
needing to use send to reach into the
00:17:18.959
internals of your class to test some
00:17:21.000
private method there you probably have
00:17:23.720
something that you can actually extract
00:17:25.199
out from that class right your like
00:17:28.919
public method any public method you have
00:17:30.720
on a ruby class is basically defining
00:17:32.559
what the public API is right so we care
00:17:35.280
about the behavior of the end result of
00:17:37.559
calling this thing but we shouldn't care
00:17:39.320
about the internals of How It's
00:17:41.120
implemented
00:17:43.520
um if you are reaching in to do that you
00:17:46.440
should probably be extracting that
00:17:47.880
entire method as some sort of class
00:17:50.240
right that then has a public method that
00:17:52.360
you then can test on its
00:17:55.840
own uh similarly stubbing out private
00:17:59.360
methods I've seen it test uh also a
00:18:01.880
smell here right you're now coupling
00:18:03.679
your test to the internal implementation
00:18:06.919
uh of that class which makes it harder
00:18:08.400
to refactor later right I've now
00:18:10.400
basically in my test called out some
00:18:12.840
specific name of a method that I want to
00:18:14.480
step out his behavior and so now if I
00:18:16.480
really want to like refactor that class
00:18:19.080
I can't just like go changing some of my
00:18:20.919
private methods around because I've
00:18:22.159
gotten too much into the internals there
00:18:24.720
so methods are private for a reason
00:18:27.520
right anything outside of that actual
00:18:29.760
class which includes the test testing it
00:18:32.400
shouldn't actually know about them
00:18:36.000
um so things that I can allow is like
00:18:39.720
stubbing out external calls uh a lot of
00:18:41.799
times let's say you're contacting a
00:18:43.240
thirdparty API you obviously don't want
00:18:45.760
to like do that in a real test um so
00:18:48.760
wrapping anything that talks that API in
00:18:51.240
one class and then stubbing that out in
00:18:53.080
all other tests so it doesn't do that is
00:18:55.320
fine uh that thing might be called
00:18:57.039
within a private method but it's like a
00:18:58.679
call to a different class um and then
00:19:01.720
sometimes I've seen people stub out
00:19:02.919
private methods just because they got
00:19:05.080
lazy uh creating the correct like active
00:19:07.679
record setup that would allow them to
00:19:10.120
test a certain scenario so I have
00:19:13.600
refactored some tests like that to
00:19:15.080
basically undo the stubbing to say like
00:19:17.000
okay yes it was a bit of a complicated
00:19:19.200
you know data setup we needed these
00:19:21.200
users and a bunch of this other class A
00:19:22.640
bunch of this other thing but like
00:19:23.919
that's actually representative of um
00:19:26.480
what we are trying to test here
00:19:31.039
uh this one's was definitely
00:19:32.760
controversial at
00:19:34.400
Gusto so subject by default is a keyword
00:19:38.440
in arpec uh that is described class. new
00:19:43.400
described class is the other keyword
00:19:45.039
which is literally what it says it's the
00:19:47.120
class uh of the thing that you're
00:19:49.280
testing so to me subject is the object
00:19:54.360
under test right and when people uh
00:19:58.480
refactor it and override it to actually
00:20:01.120
be like subject. do the thing that I'm
00:20:03.600
trying to do it looks very weird to me
00:20:05.880
then in the test to have some setup and
00:20:07.960
then just the word subject and then
00:20:09.400
assert a bunch of stuff because like one
00:20:12.000
subject is a noun it's not an action
00:20:14.799
right and two now I need to go look for
00:20:16.960
where you over road that to see like
00:20:18.440
what method are you even calling as part
00:20:20.120
of this thing um so personally I only
00:20:22.919
over override subject when I need to
00:20:25.480
pass some arguments to the initialized
00:20:27.400
method um so then it would just be like
00:20:30.240
subject block describe class. new with
00:20:32.440
whatever things I need to pass into
00:20:34.280
there uh I personally see this as
00:20:37.280
another sort of form of unnecessary
00:20:39.480
dryness that makes the test a little
00:20:41.520
more difficult to read and I think also
00:20:44.520
I've seen more Junior developers kind of
00:20:46.360
struggle with that too that like
00:20:48.000
sometimes subject is the class an
00:20:50.039
instance of the class under test and
00:20:52.039
sometimes it's me taking an action on
00:20:54.080
that instead of class under test which
00:20:55.679
makes it a little
00:20:57.120
confusing
00:20:59.960
uh similarly so we talked about the 3as
00:21:03.320
earlier um it's pretty common in arsp to
00:21:06.799
assert that a some instance of a class
00:21:10.440
or something receives some method with
00:21:12.279
some arguments right um you do that in
00:21:16.080
order to sort of isolate your class
00:21:18.240
under test and not always have to call
00:21:19.840
through a bunch of different classes on
00:21:21.279
there but I I even strangled with this
00:21:24.559
many times where there is a format where
00:21:26.360
you can say expect thing to receive some
00:21:30.120
method call and then I act on my thing
00:21:32.720
and that's it that's the test so it's
00:21:34.480
now like act and then assert uh or sorry
00:21:39.400
assert and then act which is weird so I
00:21:42.600
always do the more verbose format of
00:21:45.840
this which is allow my thing to receive
00:21:48.960
the method I care about then do the
00:21:51.200
thing and then expect my thing to have
00:21:54.200
received that method that I care about
00:21:55.960
which keeps your arrange act assert
00:21:59.320
format and it's a lot easier to sort of
00:22:01.480
wrap your head around I'm like positive
00:22:03.679
we've all been there as a developer
00:22:05.559
where it's not in that format and do the
00:22:07.720
expectation first and you get very
00:22:09.760
confused as to why your thing is like
00:22:11.279
not passing over and over again before
00:22:13.000
you realize it's because you have the
00:22:14.159
thing in the wrong order um so it's
00:22:16.679
three lines instead of two but
00:22:18.960
personally I think this is way easier to
00:22:20.559
actually follow and then you never break
00:22:22.120
that arrange acert pattern in your
00:22:26.840
test uh
00:22:28.960
this one's really just for readability
00:22:31.240
here and kind of focusing on the like um
00:22:34.880
how do I sort of keep my test small and
00:22:37.440
isolated um rspec has some cool uh
00:22:41.279
matchers so anything is one of them
00:22:43.840
which is really like you can say if you
00:22:46.240
have a hash you have a key and then the
00:22:48.080
value is just anything if I don't care
00:22:50.000
about that value in my hash that's not
00:22:51.640
the thing I'm testing against right now
00:22:53.799
um you can also specify parts of hashes
00:22:56.039
by saying like expect this thing to be a
00:22:58.440
hash including some key I care about
00:23:00.720
some key some value I care about and
00:23:02.880
ignore the other like 10 keys and values
00:23:05.320
that are in there um I tend to find it's
00:23:07.600
a lot less readable when people say like
00:23:09.760
expect this response to be some giant
00:23:11.720
blob of a bunch of uh like you know 10
00:23:14.480
different keys and values when really
00:23:16.559
all I'm trying to care about is like two
00:23:18.279
or three of them in there for the
00:23:19.559
purpose of this test so I tend to really
00:23:22.480
enjoy using rspc sort of like
00:23:24.200
composability of a bunch of different
00:23:26.279
matchers that they have in here to
00:23:27.720
really like make sure to focus on what
00:23:30.279
I'm actually trying to test in that
00:23:31.720
specific
00:23:33.600
test
00:23:35.279
um this one is a pet peeve of mine I
00:23:38.679
don't like it blocks without a title um
00:23:42.080
tests are documentation right uh they
00:23:45.720
are documentation for you and your
00:23:48.799
viewer and your future self a year from
00:23:51.799
now who doesn't remember why you wrote
00:23:53.440
this test and you get blame your code
00:23:55.240
and realize it was you who did this
00:23:56.760
thing um and other future developers who
00:23:59.039
are going to work at your company uh
00:24:02.600
especially coming from a place like
00:24:03.760
Gusto where we have 400 plus people
00:24:05.960
working on like one rails project it's
00:24:08.480
really important that you leave good
00:24:10.919
documentation for everybody right so uh
00:24:15.440
I tend to find when people just have an
00:24:16.679
I block without a title like that's not
00:24:18.159
helpful to me when I go to look at the
00:24:20.159
test file to read through like what is
00:24:22.120
this class actually supposed to do now I
00:24:24.120
sort of need to like make inferences and
00:24:26.240
whatnot instead of having a nice I don't
00:24:28.159
know if anybody uses Ruby mine but you
00:24:29.679
can sort of collapse everything down and
00:24:31.159
then just start breaking out ITP blocks
00:24:33.039
and like opening them back up it's
00:24:34.200
really easy to just read through uh like
00:24:36.559
all the different things that this class
00:24:38.720
is supposed to do and if you don't
00:24:40.520
specify a title that says exactly what
00:24:42.520
you're doing it's harder to
00:24:45.960
follow uh another source of flakiness um
00:24:49.159
don't specify specific IDs for active
00:24:51.240
record objects uh talking about the
00:24:53.520
database level for every table you
00:24:56.080
create there's a sequence that goes
00:24:58.039
along with it that keeps track of what
00:24:59.520
the next ID should be there um you don't
00:25:02.679
need to specify certain IDs sort of
00:25:04.799
similar to before like your test
00:25:06.320
database is going to get wiped in
00:25:07.640
between runs but it's very possible that
00:25:10.240
at some point you end out with a certain
00:25:12.679
test run where that ID of 15 that you
00:25:15.200
specified directly for some active
00:25:16.640
record object actually gets taken before
00:25:18.799
that and then everything starts failing
00:25:20.799
so like just always allow the ID uh the
00:25:23.360
sequence to set the ID for you and then
00:25:25.760
if you need to reference it for a
00:25:28.080
different object right like you're
00:25:29.320
trying to tie two objects together need
00:25:31.600
one uh you reference it as variable. ID
00:25:34.600
and you never have to know exactly what
00:25:36.080
that ID
00:25:38.559
is uh this is another smell one for me
00:25:42.559
so if you find yourself writing out your
00:25:45.640
it statement block and your title here
00:25:48.080
and you're saying it does this and this
00:25:51.720
other thing on there then in general
00:25:54.440
that's kind of also a smell that like
00:25:56.440
you're testing too many things things in
00:25:58.080
that one test right uh I love to use
00:26:01.440
context blocks to break those up so you
00:26:04.880
know you have a context that says when
00:26:07.480
you're an admin it does this thing when
00:26:10.720
you're this type of user it does this
00:26:12.720
other thing um I think you can also like
00:26:17.399
uh makes the title shorter and more to
00:26:19.159
the point too but uh yeah I take this as
00:26:22.279
a smell if I catch myself writing some
00:26:24.480
sort of conjunction in there that it's
00:26:26.760
probably actually two tests or more that
00:26:28.799
I need to be breaking out
00:26:32.080
here um don't use class variables this
00:26:35.360
one's kind of a testing one but just one
00:26:37.799
in general so class variables uh sort of
00:26:41.080
like environment variables are Global to
00:26:43.919
the context of that class uh which means
00:26:47.559
that every instance of that class shares
00:26:50.120
that one class variable so if it changes
00:26:52.840
in one instance it changes for
00:26:54.679
everything um my good story about this
00:26:57.679
is the very first job I had as a junior
00:27:00.520
developer out of Turing uh which is a
00:27:02.640
coding school here in Denver uh we were
00:27:05.960
doing uh hotel bookings right for people
00:27:09.039
uh so we're actually like making calls
00:27:10.760
to various agencies like Expedia and
00:27:12.760
stuff to book real hotel rooms and
00:27:14.679
charge people's real credit cards and
00:27:16.600
whatnot and in one of our classes
00:27:19.399
responsible for the booking stuff there
00:27:21.440
was some class variable that controlled
00:27:23.600
whether or not this was like do this
00:27:25.760
thing for real and production or like
00:27:27.679
okay it's testing we're going to stub it
00:27:28.960
out and there was just this giant
00:27:30.240
comment block that just said like never
00:27:32.600
ever change this if you're going to test
00:27:34.440
on here you're actually going to be
00:27:35.840
trying to book hotel rooms from running
00:27:37.760
the test Suite um which was terrifying
00:27:40.640
as a junior developer but also like itly
00:27:44.240
unnecessary uh so go back to this yeah
00:27:47.240
like use environment variables that you
00:27:48.919
can stub out and reset in context blocks
00:27:51.840
there it's a lot easier to reason about
00:27:55.640
too uh yeah and so that's it I've got
00:27:58.640
some other resources in here uh this was
00:28:01.320
actually a slightly shortened version of
00:28:02.799
this presentation because last time I
00:28:04.799
did it we got into way more discussion
00:28:06.360
and it took like two hours um so I can
00:28:09.640
probably post these links somewhere um
00:28:12.240
but if you want to look at this one I
00:28:14.279
think this Ruby com talk uh from a few
00:28:16.360
years ago when I was there is actually
00:28:17.880
it's on like non-deterministic specs and
00:28:20.440
everything it's one of the like better
00:28:22.880
Ruby comp talks that really walks you
00:28:24.559
through like all the different ways you
00:28:26.000
can have non-determinism and how to fix
00:28:27.559
them and whatnot so like highly highly
00:28:29.480
recommend watching that one