Talks

RSpec: The Bad Parts

RSpec is good, but it’s even better with less of it. Looking at a realistic example spec, we’ll learn why parts of RSpec like let, subject, shared_examples, behaves like, and before can make your tests hard to read, difficult to navigate, and more complex. We'll discuss when DRY is not worth the price and how we can avoid repetition without using RSpec's built-in DSL methods. In the end, we'll look at what's left. RSpec: The Good Parts.

RubyConf 2022

00:00:00.000 ready for takeoff
00:00:16.920 thank you for coming
00:00:19.560 my name is Caleb harth and while I
00:00:21.960 usually prefer to keep my introductions
00:00:23.820 brief at the beginning of talks and talk
00:00:25.619 employers and promotion and all that
00:00:27.300 stuff later
00:00:28.680 since you're already here and I don't
00:00:29.760 have to actually convince you to come uh
00:00:32.820 and we're probably all here for the same
00:00:34.860 reason
00:00:36.120 but in this case I do want to touch on
00:00:37.620 my background because it helps to
00:00:38.760 underline the point of my talk
00:00:41.100 I've worked in Ruby on Rails for about
00:00:42.899 10 years
00:00:43.980 and across that time I spent about five
00:00:45.600 years each of product product and
00:00:47.640 Consulting companies
00:00:49.620 I currently work at test double A
00:00:51.360 consultancy specializing in embedding
00:00:54.180 within software teams to help build
00:00:55.739 better software and better teams and
00:00:58.140 previously I worked at thoughtbot across
00:00:59.699 a variety of Greenfield and established
00:01:01.140 rail faps
00:01:02.520 you can find me on my blog online
00:01:05.220 where I write about various programming
00:01:06.780 Concepts like ethics best practices and
00:01:09.479 testing
00:01:10.920 and since the bird site is done now you
00:01:12.960 can find me on Mastodon
00:01:14.580 at dice dot camp at Caleb or just by
00:01:17.340 searching Kayla parth
00:01:20.159 throughout my career I've had the
00:01:21.299 opportunity to work in dozens of apps
00:01:23.880 each with their own test Suites and
00:01:25.320 strategies for testing and that's given
00:01:27.000 me a chance to compare a lot of
00:01:28.020 different ways of writing tests
00:01:30.479 one little let and a tiny bit of before
00:01:32.640 often seems like a good idea but I've
00:01:34.920 seen them grow organically into these
00:01:36.540 spider webs of complexity that are very
00:01:38.400 difficult for not only new members of a
00:01:40.439 team to grasp but for grizzled veterans
00:01:42.420 to reason about as well
00:01:45.259 let defines a memoized helper method
00:01:48.000 give it a name and a block to execute
00:01:49.979 and it'll execute that block zero or one
00:01:52.799 time depending on whether you call the
00:01:54.360 method store the result and return it
00:01:57.180 on subsequent calls it'll return that
00:01:58.799 stored result without executing the
00:02:00.360 block again
00:02:01.799 if you dig into the definition it's
00:02:03.240 almost exactly defined as a plain Ruby
00:02:05.280 method
00:02:08.759 a let method can be referenced by other
00:02:10.500 lets
00:02:14.099 and depending on the nesting in that
00:02:16.140 reference may be to a different method
00:02:17.640 definition in different specs
00:02:22.140 subject can also reference let methods
00:02:24.720 speaking of subject they're basically
00:02:26.400 just a let block with special semantics
00:02:27.900 since there are some methods like it is
00:02:29.940 expected to
00:02:31.140 they use the subject name implicitly
00:02:34.620 you can also give subjects other names
00:02:36.360 while maintaining those same semantics
00:02:40.020 but effectively subject is just a let
00:02:41.519 method named subject so every time I say
00:02:43.680 let I mean subject as well
00:02:47.459 before blockage executed before each
00:02:49.680 spec that it is in scope for which is to
00:02:51.540 say any describe or context block that
00:02:54.180 contains both the spec and the before
00:02:55.680 block regardless of how deeply nested
00:02:58.019 will execute the before block
00:03:01.319 now I'm going to use before here to
00:03:02.700 stand in for after as well because they
00:03:05.160 behave exactly the same just at
00:03:07.080 different times and the same thing for a
00:03:08.280 round
00:03:11.159 a before block can reference let
00:03:12.720 variables or any other methods that
00:03:14.519 would be in scope for a test including
00:03:16.200 subject or let methods
00:03:20.040 now I apologize hopefully you can see
00:03:21.780 some of this
00:03:24.000 I overestimated the contrast that a
00:03:26.340 projector and bright lights can handle
00:03:28.860 so let's take a look at an extreme but
00:03:30.659 real example of one reason that let and
00:03:32.640 subject are problematic
00:03:34.560 this is a 4500 line unit test for the
00:03:36.599 god object of an app I once worked on
00:03:39.720 before I started the project I began to
00:03:41.220 refactor it
00:03:42.360 it makes heavy use of let not just for
00:03:44.400 the main model but for many other
00:03:46.319 methods as well
00:03:54.120 drink that in
00:03:57.299 4 500 lines there are 531 let
00:04:00.180 definitions across 131 different method
00:04:03.000 names
00:04:04.260 78 of those are overridden in different
00:04:06.239 describing context blocks nine of those
00:04:08.700 are redefined at least 10 times
00:04:11.040 with a maximum number of redefinitions
00:04:13.500 of 69.
00:04:17.519 in addition to that there are 87 times
00:04:19.260 that subject is defined
00:04:20.820 and in almost every one of those cases
00:04:22.260 the type of the subject is different
00:04:23.580 from the original definition which is to
00:04:25.860 say that some method or attribute of the
00:04:27.540 system under test or a collaborator is
00:04:29.820 being referenced to subject and not the
00:04:31.320 system under test itself and that the
00:04:33.300 subject is returning a different type
00:04:35.220 of object in different specs
00:04:39.840 overall almost 14 of this file is
00:04:42.300 dedicated just to let and subject lines
00:04:43.919 not including multi-line blocks
00:04:46.800 where the values are actually being
00:04:48.000 defined
00:04:51.600 much of this structure is front loaded
00:04:53.100 in the spec in place to support an
00:04:55.080 extremely complex General fixture
00:04:58.740 General fixture is one of the root
00:05:00.060 causes for obscure tests which simply
00:05:02.220 means that it's a different it's
00:05:03.840 difficult to understand at a glance
00:05:06.000 General fixture is the term and obscure
00:05:08.400 test are terms that Gerard messeros uses
00:05:10.620 in X unit test patterns
00:05:12.720 to describe when a test builds a larger
00:05:14.520 fixture than is necessary to verify the
00:05:16.199 functionality in question
00:05:19.139 in Ruby and R spec terms
00:05:21.120 at the beginning you can see that white
00:05:23.100 block up at the beginning
00:05:24.840 there are 31 let definitions in place to
00:05:27.060 parameterize the default subject
00:05:28.560 instance this is all set up that's
00:05:30.479 needed that is not needed for most tests
00:05:33.300 but because one or two describe or it
00:05:35.280 blocks needed to reference some Behavior
00:05:36.900 All specs in this file now need to do
00:05:38.880 that work unless they Define their own
00:05:40.380 simpler subject
00:05:44.039 this is harder to visualize but the
00:05:45.660 majority of these methods are also
00:05:46.800 defined such that they're only used in a
00:05:48.660 single spec
00:05:49.680 they'd be so much simpler and clearer if
00:05:51.240 they were inlined into the IT block
00:05:53.880 needless to say it's quite difficult to
00:05:55.440 reason about given a given a let or
00:05:58.080 subject method
00:05:59.520 where what it contains and where to even
00:06:01.500 find it
00:06:02.520 it isn't uncommon in this file for a let
00:06:04.740 or subject to find hundreds of lines and
00:06:07.020 several block Scopes outside of where it
00:06:08.520 was being referenced when I was
00:06:09.660 refactoring
00:06:10.800 I had to jump 350 lines up
00:06:13.500 to find the correct let's
00:06:17.160 now I wanted to share all of this
00:06:18.479 because as we move into a more contrived
00:06:20.100 and more manageable example for the
00:06:21.419 amount of time we have to refactor we
00:06:23.699 should know that in the real world there
00:06:25.080 are test Suites that look like this and
00:06:27.000 in fact in my experience this is where
00:06:28.500 most specs that use the majority of our
00:06:30.479 spec Trend toward over time
00:06:33.000 our goal should be to have manageable
00:06:34.380 understandable and maintainable tests
00:06:36.120 that we enjoy adding to and making
00:06:37.740 changes in
00:06:45.080 so we have a situation where we're
00:06:47.100 defining a method which Latin subject
00:06:48.840 both are just with extra steps but it's
00:06:51.240 hard to track down because these macro
00:06:53.160 they're done through these macros and
00:06:55.080 they're intentionally easy to redefine
00:06:57.539 beyond the navigation issue these
00:06:59.699 methods construct themselves by
00:07:01.020 referencing other let variables and
00:07:03.120 across different specs those delegations
00:07:04.800 are being referenced are referencing
00:07:06.660 different methods
00:07:08.039 what I talked about with the different
00:07:09.300 contexts earlier
00:07:11.100 well the code seems to be doing the same
00:07:12.600 thing across different specs it's in
00:07:14.160 actuality running through completely
00:07:15.360 different code paths that are being used
00:07:16.620 and it's very difficult to track that
00:07:20.099 finally especially in the case of
00:07:21.599 subject it's common for the actual type
00:07:23.460 being returned to change between
00:07:24.539 different describer context blocks you
00:07:25.979 really have no idea what you're dealing
00:07:27.120 with when you call this method
00:07:30.300 so what are we what are our options for
00:07:32.940 refactoring out of this
00:07:34.860 perhaps the simplest option is to Simply
00:07:36.660 replace a let with an instance variable
00:07:38.400 for example let book could be ad book
00:07:42.419 well this technically checks the avoid
00:07:44.160 let box and may be slightly more
00:07:46.259 performant than a memoized lookup method
00:07:48.539 it maintains a lot of the same issues
00:07:50.639 specifically that they're likely to be
00:07:52.020 overridden or defined differently in
00:07:53.580 different contexts so there's little
00:07:55.139 chance of finding the correct definition
00:07:56.280 for a given text in a speck of even
00:07:58.560 moderate complexity
00:08:01.319 another option that solves part of the
00:08:03.419 problem is to change these let and
00:08:04.979 subject definitions to properly defined
00:08:06.360 Ruby methods
00:08:07.860 this involves memoization via instance
00:08:09.660 variable or equals some calculation of
00:08:11.520 the value
00:08:12.539 and doing this improves the odds that
00:08:14.039 automated introspection tools such as C
00:08:15.660 tags or language servers are able to
00:08:17.639 find the correct definition
00:08:19.560 the approach does not help with the
00:08:21.120 second part which is that these are
00:08:22.319 being redefined in different contexts so
00:08:24.720 depending on their nesting you're still
00:08:26.819 dealing with different methods
00:08:30.539 the third and the one that I recommend
00:08:31.979 here is the inline method refactor
00:08:34.680 this method uh was originally named by
00:08:37.200 Martin Fowler in refactoring improving
00:08:39.899 the design of the existing code
00:08:43.140 and it's pretty straightforward instead
00:08:44.520 of pulling these variables into a letter
00:08:46.020 subject Define them directly in the test
00:08:50.640 but Caleb I hear you saying this is
00:08:53.040 repeating yourself and we don't repeat
00:08:54.600 ourselves that wouldn't be dry
00:08:57.540 this is a point that's a common
00:08:58.560 simplification of don't repeat yourself
00:09:01.320 Andy Hunt and Dave Thomas and the
00:09:03.180 pragmatic programmers stated the dry
00:09:04.620 Principle as
00:09:06.060 every piece of knowledge must have a
00:09:07.800 single unambiguous authoritative
00:09:10.320 representation within the system
00:09:12.360 one of these words matters here
00:09:13.740 knowledge
00:09:15.660 the goal of dry is to avoid duplication
00:09:17.339 of knowledge and not duplication of code
00:09:20.040 now it turns out the duplication of
00:09:21.600 knowledge also tends to reduce code
00:09:23.100 repetition
00:09:24.660 but that's a side effect and not the
00:09:26.040 primary purpose of dry
00:09:27.779 it's often simplified to new programmers
00:09:29.399 myself included that they are learning
00:09:32.100 about dry as deduplicating code
00:09:34.620 even though it helps with do and even
00:09:36.660 though this helps with
00:09:37.880 deduplication of knowledge it leads to
00:09:40.560 premature extractions of code that need
00:09:42.839 to be then parameterized to support
00:09:44.279 different use cases
00:09:47.040 the default version of subject which is
00:09:48.839 defined as described class.new or Mark
00:09:51.660 concretely in our example book.new
00:09:54.300 is really more of an indirection than a
00:09:55.860 consolidation of knowledge
00:09:57.480 book.new is already a method writing it
00:10:00.240 into a subject doesn't dry anything out
00:10:01.980 but even if it did in the case of tests
00:10:04.560 I'd argue that knowledge duplicate
00:10:06.060 deduplication is not necessarily a good
00:10:07.860 goal
00:10:08.940 I've called these test specs because
00:10:10.440 colloquially that's generally how I've
00:10:12.240 heard them referenced but our spec
00:10:14.100 actually defines them as examples and
00:10:15.839 I'm glad that it does because it points
00:10:17.279 that the code that you're writing should
00:10:19.080 look like how it is used
00:10:22.320 David Bryant Copeland recently wrote Our
00:10:24.839 spec examples are well examples
00:10:27.720 the article's thesis is that avoiding
00:10:29.880 predicate matters such as expect order
00:10:32.040 not to be sent should be avoided in
00:10:34.560 favor of expect order sent to equal
00:10:37.920 false which is a good point
00:10:40.260 I have to say it's a good point because
00:10:41.519 unlike Gerard messero smartenfeller or
00:10:43.380 to a lesser extent Dave Thomas
00:10:45.360 David might actually be in this room or
00:10:47.459 at least at the conference
00:10:51.720 but taking David's Point even further
00:10:53.339 it's desirable for each example to be a
00:10:54.899 self-contained example of how to
00:10:56.399 reproduce Behavior
00:10:58.920 our spec example
00:11:01.440 real world example following that
00:11:03.660 pattern given any individual R spec
00:11:06.120 example in isolation we have all the
00:11:08.579 knowledge to reproduce the behavior
00:11:09.899 specified in the example in the code
00:11:12.600 base itself or in our own code if this
00:11:14.459 is the gem or some other shared code
00:11:19.079 drying tests imagine that I had a nice
00:11:21.180 visual here of like taking code from a
00:11:23.160 spec file and into but I didn't have
00:11:25.680 time
00:11:27.480 drying test reduces the utility of each
00:11:29.279 example by relying on shared utility
00:11:31.140 methods or Worse unshared utility
00:11:33.180 methods that convolute what's actually
00:11:34.500 happening in the tests if you can't look
00:11:36.480 at an example and understand what's
00:11:37.800 being tested how the system under test
00:11:39.839 is intended to be used and to be
00:11:41.640 confident that it actually works
00:11:43.560 then your example is not really living
00:11:45.060 up to its full potential as living
00:11:46.800 documentation
00:11:48.959 so my advice for test setup is to write
00:11:51.420 everything twice
00:11:53.279 or hopefully more than twice
00:11:55.980 Embrace that there may be a bunch of
00:11:57.600 setup that's duplicated between examples
00:11:59.940 because these examples are documentation
00:12:02.399 doing the setup inline allows you to
00:12:04.440 write only what you need in the test
00:12:06.720 and not to build out fixtures that
00:12:08.040 support multiple things
00:12:09.720 it also shows the difficulty of using
00:12:11.579 your code to Surface by surfacing the
00:12:13.800 collaborators
00:12:15.060 the arguments the class another setup
00:12:16.980 that's required to be in this
00:12:18.600 environment before you can even run the
00:12:19.920 code
00:12:22.200 complexity introduced in the name of
00:12:24.420 decreased repetition of the code does
00:12:26.579 not make your specs better and it's not
00:12:28.560 a worthwhile trade-off just to hide test
00:12:30.959 setup
00:12:33.600 if something's difficult to test that's
00:12:35.339 a smell that you're either testing the
00:12:36.600 wrong thing for example you might be
00:12:38.459 testing the return value of another unit
00:12:40.140 test of another unit tested method in
00:12:42.839 another class in that case you can
00:12:44.639 safely mock out the collaborator and
00:12:47.519 just pass the return type that you need
00:12:49.200 because you can be confident that the
00:12:50.820 other class is working correctly if it's
00:12:52.500 test pass then you can have a single
00:12:54.000 integration test that proves that they
00:12:55.320 work together
00:12:56.820 or it may be that your object has too
00:12:58.500 many collaborators and may be able to be
00:13:00.959 refactored to avoid a complicated setup
00:13:03.300 by reducing its fan in or a number of
00:13:05.339 collaborators that relies on
00:13:09.839 since Mastodon is the cool new place to
00:13:12.420 be and as a rails app I thought we could
00:13:14.279 take a look at a portion of its tests
00:13:16.260 for our refactory example here's the
00:13:18.420 full spec for the mute bang method split
00:13:21.120 into three columns to better fit on a
00:13:22.920 slide
00:13:25.079 this is a test of the mute functionality
00:13:26.940 of an account and if you'd like to
00:13:28.680 follow along later
00:13:30.120 uh it lives in Spec models concerns
00:13:32.940 account interactions spec.rv
00:13:37.680 I know that you can't read any of this
00:13:39.060 text
00:13:39.839 that's okay I'll be talking through it
00:13:41.700 as we go and use Bolding in color and
00:13:43.500 some animations to help draw attention
00:13:44.940 and help you understand what's happening
00:13:46.320 at the macro level even though line by
00:13:48.420 line you won't really have any idea
00:13:51.480 so we can see that the spec overrides
00:13:53.880 the default subject and that it's used
00:13:55.740 nine times here across about 100 lines
00:13:57.600 of code subject is a good place to start
00:13:59.519 when we're refactoring out of this
00:14:01.019 because it when it's used at all it's
00:14:03.899 used in almost every example
00:14:07.139 as I mentioned we can use the inline
00:14:08.760 method to take the body of the subject
00:14:10.500 definition and place it directly into
00:14:11.820 the body of each spec
00:14:13.260 thanks to scoping even though the
00:14:14.820 subject definition referenced account
00:14:16.560 Target and ARG notifications
00:14:19.079 all of which are let methods the code
00:14:20.639 will continue to run here and have
00:14:21.839 access to those methods
00:14:24.540 one thing to take a look at is that
00:14:26.700 these tests have a very similar and
00:14:28.440 non-flat structure which and can be an
00:14:30.660 indication that they may be testing more
00:14:32.220 than one thing
00:14:33.480 so taking a closer look
00:14:35.339 we can see if there's any overlap
00:14:37.920 each of these tests have this check
00:14:40.740 expect me to be a kind of mute
00:14:43.560 it looks like each of those are using an
00:14:45.480 expect to change block to send a mute
00:14:47.579 message and then checking this return
00:14:48.899 type
00:14:50.279 so this is an optional refactor it's
00:14:52.260 unrelated to letter subject but we can
00:14:53.880 take this opportunity if we want to to
00:14:55.320 remove that duplication of the type
00:14:56.639 check
00:14:59.220 zooming back out we can run these tests
00:15:01.079 to ensure that they pass I did they do
00:15:02.820 don't worry about it
00:15:06.420 then we can continue to inline let
00:15:08.399 methods
00:15:09.480 hide notifications and ARG notifications
00:15:11.639 are a couple of let methods that are
00:15:13.019 similarly named because they're being
00:15:14.519 passed as keyword arguments of similar
00:15:16.019 names so they're adding confusion just
00:15:17.760 by existing as opposed to being inline
00:15:19.980 in the calls so let's inline them right
00:15:22.079 away
00:15:26.579 doing that means we no longer need these
00:15:28.320 context blocks because they're no longer
00:15:29.639 providing a place to define those let
00:15:31.860 methods
00:15:33.540 so we can flatten them as well and just
00:15:35.220 take the meaningful context in their
00:15:37.199 text and append it into the IT
00:15:38.880 descriptions
00:15:42.540 now we can inline this me I like this
00:15:44.339 part of it because it looks like this is
00:15:45.660 going to be much shorter
00:15:49.980 we can inline this mute into each spec
00:15:52.440 as well and because it's used in this
00:15:55.320 before block we'll go ahead and inline
00:15:57.180 that now as well
00:16:01.800 note that at this point we're moving
00:16:03.779 some actual code into specs which was
00:16:05.519 obfuscated before that hide
00:16:06.779 notifications flag that was previously
00:16:08.880 being said in each of these context
00:16:10.079 blocks is now correctly set in the specs
00:16:12.899 so as you glance through the spec it's
00:16:14.399 easy to tell at a glance without needing
00:16:17.160 to find the hide notifications method
00:16:19.079 name which is highlighted like a method
00:16:20.459 name and not a Boolean and then search
00:16:22.560 backwards hoping to find the right
00:16:23.880 definition to find whether it's true or
00:16:25.440 false
00:16:28.860 so I'm going to fast forward a bit and
00:16:30.240 inline uh the last couple of lets
00:16:32.160 account and Target account
00:16:33.899 these are going to seem like they're
00:16:34.920 coming from nowhere because like the
00:16:37.259 original example I showed these let
00:16:38.519 methods are defined hundreds of lines
00:16:39.899 away from where they're being used this
00:16:41.699 particular file is only 650 lines as
00:16:44.100 opposed to 4 500.
00:16:46.320 uh but it uses these lets all over and
00:16:48.720 luckily it doesn't override them at all
00:16:50.160 so it's easy for us to determine which
00:16:51.899 ones to inline in this case it does beg
00:16:54.000 the question as to what benefit they're
00:16:55.019 providing by not being in line to begin
00:16:56.339 with
00:16:58.079 we're able to make the inline
00:16:59.160 definitions in place of methods in some
00:17:00.720 places and in others we use local
00:17:03.060 variables to replace the method
00:17:04.380 definition so that we can use that same
00:17:05.760 name to make assertions against
00:17:10.140 so let's take a look at one of those
00:17:11.220 refactored specs this is the before
00:17:13.260 example
00:17:14.640 uh it's extremely terse it tells us
00:17:17.339 almost nothing about what we're testing
00:17:21.059 we can kind of tell that we're making
00:17:22.919 expectations about a subject recall that
00:17:25.559 this is a test for a concern called
00:17:27.480 account interactions so we don't
00:17:29.100 actually even have a class that would
00:17:30.480 make some educated guess about what the
00:17:31.919 default subject would be with described
00:17:33.360 class.net
00:17:35.760 the innermost expectation is that we're
00:17:38.400 returning a mute but where that comes
00:17:40.200 from why it would be a mute we really
00:17:42.000 have no idea to find that we need to
00:17:43.980 scroll up in this file about 55 lines
00:17:45.840 the definition of subject Which is far
00:17:47.700 more than is what's going to appear on
00:17:49.380 most of your screens
00:17:52.440 we're also checking that a mute doesn't
00:17:54.059 change its height notification setting
00:17:55.620 where this mute is defined what its
00:17:57.539 context is or how it's related to the
00:17:59.460 subject are completely opaque to us
00:18:03.000 we also know that the be kind of
00:18:05.039 expectation is checking a return type
00:18:06.660 and that that expectation is duplicated
00:18:08.640 nine times in this file and is unrelated
00:18:10.260 to the main purpose of this example
00:18:11.760 which is to hide that test that hide
00:18:14.100 notifications flag
00:18:16.919 finally we have no hint that there's
00:18:19.020 code being executed as part of a before
00:18:20.580 block
00:18:21.660 in this block before it's ever run
00:18:25.080 after our refactor the test is
00:18:27.440 understandable in isolation every piece
00:18:30.179 of context every piece of work every
00:18:32.160 hidden step that was previously
00:18:33.419 elsewhere in this large file is now here
00:18:35.580 so we can understand and read through
00:18:37.260 exactly what's happening
00:18:44.460 for a lot of the same reasons as let I
00:18:46.500 avoid I recommend avoiding before or
00:18:49.440 after or around in most cases however at
00:18:53.280 the global level I think it makes a lot
00:18:54.600 of sense for running things like
00:18:55.799 database cleaner to run all tests and
00:18:57.419 transactions
00:18:58.799 web mock to disable up on network
00:19:00.720 connections and anything else that's
00:19:02.580 going to be run for every single spec or
00:19:05.400 even for an entire category like any
00:19:06.960 subdirectory as spec it's probably okay
00:19:08.580 to be using it before there
00:19:10.440 the smell comes when you're doing things
00:19:11.940 that are scoped to some describer
00:19:13.860 context block even though or even a spec
00:19:16.440 file
00:19:17.580 at the risk of repeating myself it makes
00:19:19.679 sense to repeat yourself in order to
00:19:21.660 have understandable tests
00:19:24.419 avoid hiding excessive setup in some
00:19:26.340 block so that it seems like things are
00:19:28.020 simpler than they are
00:19:30.419 what's worse about before than let is
00:19:32.940 that it's additive rather than replace
00:19:34.380 of a word that I looked up just that I
00:19:37.260 could use it
00:19:38.340 here
00:19:39.900 whereas redefining let or subject will
00:19:41.640 not execute the code that will have
00:19:43.919 would have been defined by the method on
00:19:45.480 that name outside the current scope
00:19:46.740 before after and around will combine
00:19:49.140 so this can lead to a lot of setup code
00:19:50.700 that's happening outside of the scope of
00:19:52.140 your test you're running and can be
00:19:53.820 difficult to track down what all of it
00:19:55.200 is
00:19:57.000 shared examples shared context behaves
00:20:00.000 like and similar rspec tools have the
00:20:02.039 same problem as letter before since
00:20:03.720 they're almost certainly using those
00:20:04.980 tools but additionally because they need
00:20:07.260 to use indirection so they can be
00:20:08.940 reusable they have the additional
00:20:10.740 problem that they explode the complexity
00:20:12.360 of code because they need to be run
00:20:14.940 every single time they're included so
00:20:16.559 they may be being run and running all of
00:20:19.080 their before blocks and all their let
00:20:20.340 blocks and all of their round blocks
00:20:23.460 a dozen times every time you include
00:20:25.559 them because they're being
00:20:27.179 they're being included in multiple
00:20:28.799 places
00:20:30.000 these hide complexity in the name of
00:20:31.559 decreased repetition and often require a
00:20:33.539 lot of their own setup to be used again
00:20:35.640 I'd recommend moving the important specs
00:20:37.380 for each class inline into the main spec
00:20:39.240 files rather than trying to extract them
00:20:41.400 using these tools
00:20:44.220 and for things like a module or a
00:20:46.080 concern rather than testing that every
00:20:47.880 single class that uses a module behaves
00:20:50.160 as if it does
00:20:51.480 try to test it directly or possibly
00:20:53.100 generate a test an example
00:20:55.860 implementation for the test that can be
00:20:57.960 used to exercise the module and serve as
00:20:59.880 a reference to show you exactly what
00:21:01.320 your dependencies are
00:21:04.380 so in summary use our spec but only the
00:21:07.380 good parts
00:21:08.340 don't use let don't use before and don't
00:21:11.039 use shared anything
00:21:13.260 once again I have been Caleb Hearth
00:21:14.820 thank you so much for coming to the talk
00:21:16.380 and if you'd like to ask questions or
00:21:19.020 discuss our spec test double Mastodon
00:21:21.360 Dungeons and Dragons
00:21:23.280 please do come find me in the hallway
00:21:25.679 I also want to thank my employer for
00:21:27.419 sending me to rubyconf this year if
00:21:28.919 you've ever felt frustrated when you get
00:21:31.140 stuck on a problem we want to help with
00:21:32.880 that let us know what topics you wish
00:21:34.559 there were more resources on such as
00:21:36.299 talks blog posts or screencasts by
00:21:38.820 taking this survey
00:21:41.880 testable loves pair programming because
00:21:43.980 it can help us to get unstuck if you'd
00:21:45.600 like to pair with a double agent please
00:21:46.740 request a free virtual pairing session
00:21:48.419 with a testable developer at this link
00:21:50.700 thank you very much