Summarized using AI

Testing Should Be Fun

Noel Rappin • November 01, 2012 • Denver, Colorado • Talk

In the presentation "Testing Should Be Fun" by Noel Rappin at RubyConf 2012, the focus is on the importance of making testing enjoyable, reflecting on the challenges and frustrations often associated with Test-Driven Development (TDD). Rappin highlights how TDD was initially perceived as fun due to its quick feedback loops and problem-solving nature. However, over time, many developers encounter significant hurdles such as slow tests, cumbersome test suites, and challenges in maintaining clear and effective tests.

Rappin emphasizes a few key points throughout the talk:

- Historical Context: He discusses Kent Beck's foundational work on TDD and illustrates the evolution of testing paradigms from simple, incremental testing to the more complex TDD practices adopted today.

- Joy in Testing: He connects the programmer's satisfaction in writing tests to the joy of coding well, suggesting that both aspects should complement each other. Rappin questions whether developers still find joy in writing tests or if they have succumbed to merely having tests authored without genuine engagement.

- Testing Challenges: Several common pitfalls are addressed: slow tests, unreadable tests, and tests that mask poor design rather than improve it. These challenges detract from the enjoyment and effectiveness of TDD.

- Refactoring Tests: Rappin advocates for viewing tests as part of the maintainable codebase by guiding the audience on how to refactor and improve tests. Suggestions include

- Structuring tests to enhance readability and maintainability.

- Breaking down long tests into smaller, focused ones.

- Encouraging clear naming conventions that reflect test intentions and behavior.

He also stresses the importance of regularly reviewing test suites for relevance and clarity, urging developers to delete unnecessary tests. Rappin concludes with the notion that tests, when well-implemented, can significantly ease the development process, ensuring sustainable improvements and feature deliveries. The overall message is that by continuously refining our testing strategies, we can restore the joy in testing and ultimately enhance our development work.

At the end of the presentation, Rappin shares his contact information and resources for further learning about effective testing practices.

Testing Should Be Fun
Noel Rappin • Denver, Colorado • Talk

Date: November 01, 2012
Published: March 19, 2013
Announced: unknown

Kent Beck's first articles about Test-Driven Development. I don't think I'm alone in being drawn to TDD at first because it was fun. The quick and consistent feedback and the ability to turn complicated problems into a series of smaller problems to be solved made TDD development seem more like a game than work.

Eventually TDD became less fun. Slow tests made running test suites a cruel joke. Test code gets bulky, and is hard to refactor because, of course, tests don't have their own tests.

Let's fix that. We'll show some notorious testing joy-killers, like slow tests, hard to read tests, tests that paper over bad designs rather than driving new ones. And we'll squash them, and regain testing delight.

RubyConf 2012

00:00:14.719 let started hi I'm nor rapping testing should be fun
00:00:21.160 um that's it thanks U okay that that's tempting but
00:00:27.960 I'm not going to do that um I work a group engineering where we have a lot of really smart Engineers doing a lot of
00:00:33.480 cool things um we'd like to do one of them we are currently hiring uh in Chicago pal Seattle and possibly other
00:00:40.920 locations as well you come see me at some point today I go to group.com techjobs um that's the end of that
00:00:48.480 commercial uh I'm also no rap on Twitter should you feel like communicating with me over there
00:00:54.239 okay I want to talk about testing today a little bit obviously and I want to start with a little bit of is um who's
00:01:01.480 this guy k k somebody got it really quick yes k um okay why am i showing Kent backck
00:01:09.720 here anybody yeah so Kent was uh if not the inventor of of test from the test for
00:01:16.640 style certainly a very strong popularizer of it um in
00:01:22.960 particular Kent wrote this article in 1998 this is one
00:01:29.520 of the things where I'm starting from here you can't read it it says junit test infected programmers love writing
00:01:35.920 tests um if you go looking for this and you are kind of archaological bank this is still living at sourceforge.net j.
00:01:43.119 sourceforge.net um and as you can tell the HTML style has not been updated
00:01:48.240 really uh since 1998 um if you can't read the internal paragraph there it says testing is not
00:01:54.680 closely integrated with development this prevents you from measuring the progress of development you can't tell on something starts working or something
00:02:01.119 stops working using junit you can cheaply and incrementally build a test Suite that will help you measure your progress spot unintended side effects
00:02:08.200 and focus your development efforts so that's very much the same pitch that we that is still being made for test
00:02:13.560 develop test first programming uh 14 years later two things that are interesting about about this article um
00:02:20.400 looking back on it one is if you go in and read it and I you probably should um
00:02:25.640 is that back here is not really talking about what we would consider test first yet um he's talking about incremental
00:02:32.599 testing but he's actually writing it up as code a little test a little code a little test a little uh he hasn't gotten
00:02:38.480 to the Insight yet that that the tests uh might be better off coming first and the other thing is this idea that
00:02:44.879 programmers love writing tests and the programmers would left on their own really love doing it and I I I really
00:02:51.440 took to this um maybe I'm just easily suggestible but I started using a junit
00:02:56.519 and I really enjoyed it um how many people know what this is show hand anybody want to shout out
00:03:05.480 huh I can't hear you if you don't say anything this is the junit test Runner Circa of 2001 or something like that
00:03:12.319 this was my arpec in 2001 um and I actually had when I was
00:03:17.760 searching for this image uh to find it because I you can't really I don't think you can this is the uh runable interface
00:03:24.440 anymore I had a really warm uh wave of nostalgia when I saw it like I used this
00:03:29.480 inter face for a long time and I really Associated it with doing things well and being you know enjoying the things that
00:03:36.080 I was doing okay so who's
00:03:41.120 this I think you got it now okay thank you Abdi Grim um notable for many things
00:03:47.280 the one I want to talk about right now is a talk that he gave almost exactly one year ago called confident Ruby uh
00:03:53.280 Ruby Midwest it's available uh online I recommend it AI said something interesting at the talk at the beginning
00:04:00.040 of the talk uh that really resonated with me the talk is largely about using good programming practice to improve
00:04:06.560 sort of at a micro level your Ruby code um but one thing he said right at the top he said I used to think this talk
00:04:12.599 was about programming but now I see that this talk is fundamentally about
00:04:18.120 joy and it occurred to me it has occurred to me that the programmers love
00:04:25.120 writing tests that Kent is talking about and the this talk is fundamentally about
00:04:30.160 joy that Abdi is talking about are very similar if not actually the same thing
00:04:36.199 it's the satisfaction maybe satisfaction is not a strong enough word but it's what
00:04:41.560 we get when we feel like we're doing uh our when we're coding well I was going
00:04:46.680 to say do our jobs well but I think it goes beyond doing our job I think that that many of us are here especially many
00:04:52.000 of us are here because this is something that we enjoy doing and we enjoy doing
00:04:57.360 well um but I also work with a lot of people and talk to a lot of people who
00:05:03.960 uh complain about their test suite and then having to write tests first not necessarily having right test first but that that the tests are buggy they're
00:05:11.440 they're long they're slow they're a pain in the neck how many people feel that way about something that they have worked on or or near that it's that the
00:05:18.680 testing has become a burden probably that is why you are in this room right now I hope if you think you're yeah this
00:05:27.440 is not the game programming talk if you haven't figured out out yet
00:05:33.960 um so the question is is do we still love writing test the Ruby Community really has a test culture in a way that
00:05:40.520 other programming communities don't but do we still love it or like a lot of novice writers uh do we just love having
00:05:48.400 have the test written uh or like a lot of the the the
00:05:55.280 Hipster prot the Hipster stereotype of Ruby programmers do we just love saying that we we've written
00:06:01.360 tests and what is it that makes testing fun and fun may not be the the word the
00:06:08.120 best word here maybe it's satisfying enjoyable useful um but but that that feeling what in it in tests
00:06:16.120 um what what what brings us that and I want to suggest that there are two
00:06:23.360 separate pieces to that and then I'm going to expand on that idea a little bit there I'm going to throw two words
00:06:28.560 up here that are somewhat synony synonyms but I'm going to be using them to reflect different aspects
00:06:35.400 of the testing experience that I can think of as the shortterm and the long term in the short
00:06:42.039 term what's enjoyable to me about tdd is the idea of feedback you have this idea that I have
00:06:49.400 an immediate sense not only of what my code is doing but what I'm going to do next and whether what I just did worked
00:06:56.000 and there's very there's a very strong game relationship to this I have I've set myself a small puzzle to solve I
00:07:02.240 solve it yay I mean the the the the the uh positive feedback cycle of that is is
00:07:08.759 very uh enjoyable and a little bit addictive perhaps
00:07:14.639 um but that's the shortterm that's what happens as you are building your tests
00:07:20.400 but there's also a long-term aspect to it that I'm sometimes I'm calling communication and this is what your test
00:07:26.639 Suite potentially brings to you uh as it continues on because the test that you
00:07:32.280 write in a tdd process don't vanish Into The Ether once your feature once your
00:07:39.240 feature is done they live in the test suite and a lot of what I'm going to talk about here for the next 35 minutes
00:07:44.840 or so is going to be focused on uh the implications of that that these tests live on forever and there's a
00:07:52.000 communication aspect to that some of which is that the tests are somewhat serve as docum documentation some of
00:07:58.720 which is that the test serve uh as a marker of your intent uh some of which
00:08:03.919 is that the tests serve to communicate whether everything you think the test is doing still works but this is obviously
00:08:09.000 this long-term signal that you get from a good test Suite is obviously to me
00:08:14.199 part of what makes this process valuable fun whatever we can look at this from the
00:08:19.520 other side too uh what makes this not painful what makes us dread going
00:08:25.639 into the rpec what makes us dread typing spec at a command line um and there are a couple of things like
00:08:32.200 this um long run times are a big one uh tests that are flaky intermittent failur
00:08:37.680 intermittent failures are are really morale sapping I think um tests that are
00:08:42.919 impenetrable you don't know what there is you're afraid to look at the six the test from six months ago because you don't know what they're doing and you
00:08:48.160 don't know even whether they're still needed um all of this stuff is kind of a drag on your
00:08:54.680 own work I'm kind of building up towards saying productivity but that's not really what I mean it's just sort of a
00:09:01.200 drag on uh the way that you the way that you can bring this test driven process to
00:09:07.240 bear and I think some of this can just be summarized that as test
00:09:12.640 sues get big they become unwieldy and I
00:09:18.279 think that as a group Community we haven't really solved that problem of how to deal with a test Suite as it goes
00:09:24.920 from the thing I do to solve this immediate problem to the s to the thing
00:09:31.000 that is an artifact of my code just like my application is an artifact of my code
00:09:36.160 or uh look at it this way from my perspective the tdd process is red green
00:09:44.600 refactor and I move on the test fails the test passes I clean up my code and
00:09:49.680 then I'm on to the next test from the perspective of the test the process looks like
00:09:57.360 this red green refactor green green green green green
00:10:04.000 Green the test continues to run right and that's this is the best case I thought about sneaking in a red there in
00:10:10.079 the middle there uh but I'm sure all of us you know none of us writes that kind of intermittent test that would just
00:10:15.360 fail once every 12 times or something like that no no of course not not you not me not you
00:10:22.440 um tests are run way more often than they're written this is a weird kind of
00:10:28.600 pseudo analogy ology to the idea that test that code is read more often than it's written tests are run more often
00:10:34.600 than they're written um at Groupon we run our
00:10:40.360 continuous integration server on a build farm that is probably and I say this not as a point of Pride necessarily uh we
00:10:47.279 run it on a CI server that is probably more powerful than many of your production web servers um and in the
00:10:56.440 uh that's in the service of getting our test Suite down to a mere 20 minutes on all that Hardware um nobody knows what
00:11:03.120 the entire test Suite runs at on a laptop because nobody runs it on a laptop uh because it's infinite who
00:11:08.839 knows could be I know that continuous integration server runs upwards of a 100
00:11:14.360 times a day and that doesn't even count the number of times that a developer
00:11:20.160 will run part of the test Suite as they're building something so we have tests that run you know that are
00:11:25.440 individually run I obviously like this is a point like our production code obviously gets run more often than our
00:11:30.480 tests but we spend a lot of time thinking about how to make our production code faster and more efficient um our tests get run over and
00:11:37.120 over again but we don't necessarily spend a lot of time thinking about the implications of
00:11:43.800 that what does it mean when you add an extra test when you write a new test you you're living with it for the
00:11:50.279 potentially for the rest of your product's life cycle you're adding time uh you're potentially adding something
00:11:56.399 else that somebody else needs to understand you're adding another point of failure I am not in any of this
00:12:02.680 saying that you should abandon tdd TD tdd test first bdd whatever you call it
00:12:08.399 has been you know the focus of how I've arranged my professional work for over a decade I love it I think it's a great
00:12:14.880 process I'm not saying that you should abandon it I'm just I am saying though um that the way that we do it right now
00:12:23.880 tends to break down over time and that we could probably think there are probably ways that we can do this that
00:12:29.440 will make this more pleasant um more fun test can be Legacy I think is one of the
00:12:35.720 points Michael feathers writes in his in the working effectively with Legacy code
00:12:41.480 book that was written in 2004 uh he defines Legacy code is code that doesn't have tests and I think
00:12:48.680 eight years on that seems kind of quaint the idea that you could have Legacy code
00:12:54.360 the idea that tests in and of themselves would keep code from feeling like Legacy code
00:13:00.199 uh is is uh seems like an artifact of a time when there weren't very many people
00:13:05.480 doing test driven development because I don't know about you but I have seen and work with code bases that have full test
00:13:12.920 Suites but still feel uh still have the kind of Legacy uh feel in terms of being
00:13:19.320 resistant to change some of you are nodding I think that might include some of you in the
00:13:24.480 audience I kind of call this The Uncanny testing Valley um
00:13:30.760 you reach this point where you're committed to testing and you have put so many resources into testing that you
00:13:37.639 feel like you need to continue to aband to continue to put all of these resources into testing because you're doing it but yet you don't have enough
00:13:45.480 tests your tests aren't good enough for you to be confident when you make large changes so you kind of have the worst of
00:13:52.759 Both Worlds you have the cost of maintaining a large and somewhat flaky test cycle but you don't get the
00:13:59.160 benefits of feeling like you can make changes with
00:14:05.279 confidence Details Matter here when you're making tests when you're writing tests things that seem like small
00:14:12.160 details when you're writing tests in much the same way that many of us have embraced maybe all of us have embraced
00:14:17.680 this idea when it comes to our code we've embraced the idea that small details matter in the long-term
00:14:24.480 maintainability of our code and the long-term uh suitability of that I think I think that to a large extent we need
00:14:31.440 to embrace that mentality when it comes to our tests too tests are part of our code base in fact it's even more
00:14:38.399 imperative that our tests get written well because our tests are part of our code base but our tests don't have their
00:14:43.600 own tests um if our tests are bad or fail or hard to read we don't have a
00:14:48.720 level underneath this thank God because otherwise we would be coding in an erra painting um but we don't have a layer
00:14:55.199 underneath that to catch that if we make a mistake in our test so it's imperative that our tests are as clean and as clear
00:15:01.720 as we can make
00:15:07.720 them one of the things to me the higher order principle behind a lot of what was
00:15:15.959 XP and we now kind of group into agile the the name agile is the idea that
00:15:23.959 things that are complicated and take a long amount take a large amount of time and have high risk we can
00:15:29.480 that by doing them continuously in small pieces so testing is hard we do test
00:15:35.160 First Development so we build our test incrementally over time in small pieces um integration is hard so we can
00:15:42.600 integrate continuously um I assume that almost all of us work in a work in a
00:15:47.839 project where it is part of our project's culture that people move their code back to a common code base daily if
00:15:53.639 not more frequently um that is a staggeringly new idea 10 or 15 years ago
00:15:59.399 that was crazy that was just something that you couldn't do um code review is hard so we pair
00:16:06.959 program which is effectively continuous code review refactoring is hard so we do it in pieces at the end of every code
00:16:13.639 cycle um and I think what I'm suggesting here is that part of this cycle part of
00:16:21.120 this refactoring cycle also includes the idea that tests are code that needs to be
00:16:26.519 refected and I want to suggest some possible individual refactoring so I am
00:16:32.759 going to show some code yay I want to suggest some things that
00:16:37.800 are possible test refactorings part of this is because I think that these things are good ideas and I see tests that don't do them part of it is I want
00:16:45.319 to kind of start the idea of there being a vocabulary around these kinds of refactorings the way we have built a vocabulary around regular refactorings
00:16:51.759 and part of this is just to hopefully put the thought in all of your heads that looking at your tests with
00:16:58.199 this kind of factoring I uh is also a valuable use of your time okay I also think to start off here
00:17:06.720 that even if you're not using cucumber even if you're not using rspec given or something like that the structure of a
00:17:14.919 test thinking about the structure of your test as having a given when then
00:17:20.000 structure um is valuable that there's there's there's a benefit to you to
00:17:25.439 thinking about your test in terms of uh first part is a given part where we
00:17:30.799 gather some data second part is a when part we do something in the program and the third part is a then part we
00:17:37.440 evaluate that that's a rhythm or a structure that is very adaptable to most of the tests that you're going to write
00:17:44.080 and if your tests seem to be abandoning this structure for one reason or another um there's probably a better way to do
00:17:52.039 it so and then also some of these uh suggestions that I'm going to have here come on the setup side and a couple
00:17:59.000 things to think about when you're actually running your tests as well in setup um one more factoring that
00:18:06.600 I think is useful is applying a behavioral name to your tests and and in general um thinking a little bit about
00:18:12.919 the naming of your tests um naming tests is sort of in the same category as general documentation or commit messages
00:18:20.799 uh we all know we should do a better job of it than we actually do and we never quite get the feeling that that uh we've
00:18:26.200 done a better that we we never quite work up the energy to actually do do that better job um but we wind up then
00:18:32.159 with here's one kind of bad case here the test names are here kind of lazy uh it handles true input it handles false
00:18:38.799 input uh this provides no information about my intent uh about what's actually
00:18:44.320 happening here um and it makes the test very hard to keep go keep up over a long time when someone else comes to this and
00:18:50.480 needs to figure out what's going on uh it's a little bit better but not ideal to structure this by Method you
00:18:56.679 see this quite a bit um in this case the outer describe block is um um the method
00:19:03.400 name potentially and then the inside block is describing um how that method acts uh we're describing this in terms
00:19:11.360 of the uh internal implementation of this in my actual code um you know in in its
00:19:18.960 own weird way that's a dependency um this test is is the test name is sort of dependent on the structure of the method
00:19:26.159 and the structure of these pieces uh remaining the same uh if that changes but we don't change the code name the
00:19:31.600 test name you know then we're confusing future uh future developers and in this
00:19:37.840 case for future developer you should as always read you in six months that's if you don't have empathy
00:19:44.320 for other people at least have empathy for yourself in the
00:19:54.280 future right yes yeah I mean the Assumption here if the method so the question was if you the if the if the um
00:20:01.080 if the method is if the test is named after the method name would you assume that that method is called and that if the method name changes that it would
00:20:07.240 break yes that's hopefully ideally um explicit breakage is always a good idea
00:20:12.520 um but I prefer uh describing the test in terms of the behavior they're trying to get I
00:20:18.840 mean really the b in behavior-driven development doesn't just mean that we use it and should and describe instead
00:20:24.799 of assert what it means is that we think about what we're doing describing we're doing in in terms of the behavior that
00:20:31.679 we're expecting and not the implementation of it so in this case um
00:20:36.919 I'm describing when checking for validations and I'm saying it allows an admin user access it denies a regular
00:20:41.960 user access it's generally a good idea in the test name uh to describe what the test is actually doing uh and when what
00:20:49.120 the test what the expected behavior of the test is and one reason for that is 6 months from now when the test fails not
00:20:54.919 if the test fails when the test fails uh and somebody because there's been another change in the code and somebody
00:21:00.559 has to decide um is the test right or is the code right the name of the test
00:21:07.520 provides a hint as to the intent and gives that somebody a a clue as to
00:21:12.679 whether they need to change the test to conform to the new code or whether you've introduced a
00:21:19.919 bug uh another refactoring here is extract test splitting a long test into two smaller tests so here's a sample
00:21:27.000 test um this is a a relatively non- egregious version of this um but I'm creating a user I'm creating for example
00:21:33.679 a Blog blog post testing that a user should have visibility to the post changing the role of the user testing it
00:21:40.440 again changing the post testing it again this is to my mind if you think
00:21:46.799 about this in terms of a given when then structure what this test is doing is I'm doing a given where I'm gathering data
00:21:54.360 uh I'm sort of doing a then there there isn't really an action here except the the Association I guess is implicitly in
00:22:00.159 action uh I'm doing a then to test something and then I'm gathering more data and I'm doing the then again and
00:22:05.720 then I'm gathering more data and I'm doing the then again um it's not horrible I think almost everybody here
00:22:11.600 can figure out what this is doing but um it does
00:22:16.919 separate three different concerns here three different three different logical
00:22:22.360 concerns in a way that that makes again makes it hard to tell later on when the test fails what accurate and so I would
00:22:30.679 recommend this is part this was part of how I would recommend it changing it um one test at a time not necessarily we'll
00:22:36.679 talk a little bit more about single assertion style in in a few minutes but here we have two separate Givens uh and
00:22:43.000 then two separate ven at the end and now it's two different tests Each of which has its own intention name um and this
00:22:49.799 is over time a little bit easier to deal with now there's a slight performance
00:22:54.880 cost and we'll talk about that a little bit too um and one of the things here
00:23:02.799 is uh so okay sorry the next thing and a lot of time as in a lot as in other code
00:23:10.279 situations um creating one refactoring uh makes the underlying structure
00:23:16.400 visible uh to see what the next refactoring is and in this case it makes it very clear that we also have a common
00:23:22.559 setup that we can extract out into uh moving from a duplicate setup in multiple methods uh moving to a um
00:23:30.919 before structure in rspec or let structure in rspec uh which will both do the same way same both have the same
00:23:36.559 effect and in this case we have a common setup um there's some good and bad in
00:23:43.480 this as in a lot of refactoring sometimes there's a trade-off uh in terms here in terms of what uh is an
00:23:48.679 improvement what's not an improvement to my mind one of the improvements of this is it makes it explicitly visible uh it
00:23:54.880 it separates the important part of the test itself in the blocks from the setup
00:23:59.919 in a way that makes it easy to see what parts of this are setup and what parts of this are actual logic um there is a
00:24:06.960 danger if you make the setup if the setup is in such a way that the setup gets kind of far away from the actual
00:24:12.520 code um then the then the test kind of feels like magic um this is a problem one of the problems with global fixtures
00:24:18.679 U with a lot of explicit data um you you got tests that just say
00:24:23.799 you know oh this should be 47 why well because there's data that you can't see in another file that says that this
00:24:29.440 answer should be 47 so you do need to be aware of that but in general it's a good idea to factor out common setups for the
00:24:35.880 same reason it's a good idea to factor out in in in regular
00:24:42.039 code um when you're Gathering the data a really important kind of refactoring is
00:24:48.760 to remove or not create extraneous objects so here's a test uh we're
00:24:55.919 determining whether or not we can we can extract users with high ratings on some kind of 1 to 10 scale um and we're
00:25:03.320 actually explicitly creating uh 10 users here a user with each individual rating
00:25:09.360 um and then we're checking that uh the two users with high ratings are are the
00:25:14.840 ones that we find uh the problem here is that we are creating 10 users uh and worse we're
00:25:21.159 saving them all to the database um in fact we don't need to create 10 users to
00:25:26.640 drive this so we're reaching a little bit of a distinction here between the purpose of a test in a tdd environment to drive the
00:25:35.320 code that we're looking at and the purpose of a test may be to perfectly 100%
00:25:40.399 verify uh the behavior um this is creating too many objects in a tdd setup
00:25:46.360 to to to drive this test and that's going to slow things down and slowing things
00:25:53.080 down by the amount of you know creating eight extra objects into the database which doesn't much when you have one
00:25:59.480 test and you're running it once but it seems like quite a bit when you have a thousand tests like this and you're running it 150 times a day
00:26:07.640 um that's where you get to the point where you have to give everybody a week off to clean up to find the fastest tests uh and clean them off so one
00:26:16.279 solution here is um create just enough objects usually just enough objects to
00:26:22.399 manage your boundary conditions if you have more than one boundary condition that implies that you need more than one test uh in this case we're creating the
00:26:29.080 low user and the high user which are just at my presumed boundary of what a
00:26:34.640 high rating should be um and so I'm testing that I just get the high one um if I find a bug in this because my
00:26:41.000 boundary condition is incorrect then I write another test you know try to solve all the problems of the world in one
00:26:48.799 time there's actually this actually again this brings us to another related
00:26:53.840 somewhat related issue which is to remove database creation um those of us who work rails all the time uh have sort
00:27:00.760 of implicitly been allowed to buy into the idea that you can call something a unit test when it hits the database uh
00:27:06.679 which is a fairly large thirdparty dependency on something that is nominally a unit um but rails makes it
00:27:11.840 real easy to do so we do it because it's really easy and it's sometimes valuable um you really really shouldn't hit the
00:27:19.600 database um in a unit test uh if you feel like you need to hit the database
00:27:24.919 in a unit test that probably means that the structure of your code uh could stand to be improved so uh we
00:27:31.760 have an unnecessary database hit here I'm creating two users and I'm saving them to the database but there's nothing in the rest of the test that needs those
00:27:38.840 things to be persistent so we can just change those uh to user new we're not getting a database hit um and this is
00:27:46.120 actually uh even with two objects you can often measure the difference the
00:27:51.679 speed difference on a single test for that so let's take the same test and see
00:28:00.000 how it live how it works a few a few months later we've added some
00:28:05.279 validations to the user object and now suddenly in order to create these simple user objects we need to make them pass
00:28:11.440 the validations uh and now we've had to add a password and a password confirmation and a username and an and
00:28:17.559 an email because user objects need to have those things otherwise we get validation failures uh so in this case
00:28:24.039 we might not because we're just creating them and not saving them um but the problem here is we now have introduced a
00:28:30.320 lot of noise in this test it is much much harder to look at this test and see what the important piece of data is the
00:28:36.640 important piece of data here is that the high user has a rating of nine and the low user has a rating of eight um but
00:28:42.399 that's completely swamped by the amount of data for me this is the use case for
00:28:48.960 factories for me the use of a factory is somebody like that okay for me the use
00:28:54.840 case of a factory and and Global fixtures can solve same problem um as long as you're careful both of these can
00:29:01.519 solve the your the problems you need to be careful with how you use them but the use of a factory here is to create a
00:29:08.120 default object such that when you need to have one of those objects in your tests uh you can explicitly specify
00:29:16.880 only the attributes of that object that need to change that are valuable for the test so we're back to uh being able to
00:29:25.080 easily see that one user has a rating of nine and the other user has a eight and this test is much much easier to to
00:29:35.919 understand where you get in trouble with factories uh is where you use them to specify chains of Association which you
00:29:42.440 absolutely should not do uh you should never my opinion uh use Factory girls
00:29:48.279 Association stuff to automatically create associations if you need an association for a specific test you should create the association in the
00:29:54.399 test um if you don't do that you get you wind up being in the situation where uh
00:29:59.840 you create one object for a test using a factory and you get like 20 or 25 other objects on an object tree coming along
00:30:05.600 with it all getting saved to the database and uh not for nothing but that's removing that was uh how we got
00:30:12.279 the group on Main web app code uh test Suite down from 40 minutes to 20 minutes
00:30:17.600 because there had been so much of that kind of factory usage where you getting trouble with fixtures is where you
00:30:24.240 depend on specific pieces of the data that are hidden in the fixtures um you
00:30:29.559 know you go oh uh I'm going to grab Fred and I know Fred is 6 feet tall because
00:30:34.799 in the fixture file it says Fred is six feet tall um that's where that gets in trouble that that that becomes hard hard
00:30:40.200 to see and I you know had a project a long time ago where we had all the search all these tests for a search
00:30:46.519 function that depended on a particular Universe of objects being in the fixture
00:30:51.960 files and somebody would add a new fixture and suddenly 25 search tests would break that's that's bad don't do
00:30:57.639 that uh this is sort of similar to this is
00:31:03.440 this is the Cucumber version of this um the Cucumber version of extracting to extracting tests here we have uh sort of
00:31:10.120 a workflow in cucumber um it's a little bit easier to see why you might want to
00:31:15.519 do this in uh cucumber than in rspec but I still
00:31:20.919 think it's kind of a bad idea I still prefer to try and do this in separate pieces and build up a chain of smaller
00:31:27.039 tests to to test my workflow um except in some cases unless there's like a
00:31:32.240 super legitimate need to test the entire workflow as one unit uh and test
00:31:37.279 individual pieces along the way uh I'm not a big fan of of doing that of trying to do multiple hits in one
00:31:45.480 test I also recommend you invoke your actions at the right level um which is another way of saying that cucumber
00:31:50.720 makes a lousy unit test framework um my opinion of cucumber here is cucumber is
00:31:55.880 a very good way to Focus your happy path development um know what you need to do
00:32:01.320 next and most importantly know when you're done um cucumber is not a great way to uh unit test individual behavior
00:32:07.840 of individual methods and if you find your cucumber tests in particular that you are writing a lot of them a lot a
00:32:14.519 lot of them and they become a real burden on it you should really take a look and see if you're testing things in
00:32:20.440 cucumber that probably could be better tested in a more focused Way by unit testing a controller or unit testing a
00:32:26.480 model
00:32:33.159 um single assertion as a test style so here this is a test written as single
00:32:38.240 assertion um the idea behind single assertion is that like in an rspc sense
00:32:45.279 the idea is that in a single assertion test test you put the given and the when
00:32:51.960 in your before block and you only put the then in your it block and you only have one per it block so if you look at
00:32:57.679 this test um I have a a common describe block a common Suite I have a given
00:33:03.799 where I'm grabbing a user uh and giving a deals for and I'm adding um a deals type being restaurant and then I have
00:33:10.639 individual assertions on the different pieces of that alternately I can write this as a single test where the give and
00:33:16.320 the when and the and both then are in the same test and you'll see arguments either way
00:33:22.480 on this the single assertion tests have the advantage that they can fail independently uh and you can give them
00:33:28.080 independent intention revealing names and that's an advantage on the other hand they're often slower um you're
00:33:33.519 doing the setup multiple times so if you're doing a complicated setup they're often slower um I don't have a 100%
00:33:40.559 recommendation on this I tend to prefer single assertion um but if I have two
00:33:45.919 assertions that are very very tightly coupled for some reason and I don't expect them to ever fail separately I
00:33:50.960 will often put them uh in the same test as as much as a as much as anything else as a marker for my intention that those
00:33:57.880 two assertions are very tightly coupled you should also make sure you're
00:34:04.000 testing the thing at the right level or again or testing the thing that's actually part of the class that you're dealing
00:34:09.960 with see this a lot in rails controller tests as as as maybe the most obvious
00:34:15.159 example um here's a controller test where I'm creating two users I'm running my search uh and I'm determining that
00:34:21.760 the search returns a particular object um that's not really a controller test
00:34:27.240 um although I would imagine that everybody in here who's done rails has written a test like this I certainly have um this is really a model test what
00:34:33.919 I'm really testing is that the the behavior of the models uh the the behavior of the
00:34:39.720 model's search method um from the controller's point of view the only thing we care about is that we call the
00:34:45.119 search method you know and that's a common thing when you have code that integrates multiple layers of your
00:34:50.359 program you want to make sure that the tests are isolated as much as the code is isolated that when you when you are
00:34:57.079 boundary testing over a level that's a really good place uh to have a stub or a mock um the problem here is that if your
00:35:04.560 tests cross the problem here is very similar to regular code if your tests cross a layer you have many you have the
00:35:10.520 same dependency problems you're going to have if your code cost uh crosses a layer which is that your test becomes
00:35:16.240 brittle against changes in the code um and it also because we probably or we
00:35:22.839 frequently will also have a model test to test the search um you have some a
00:35:28.040 tests uh and that's going to slow things down again a lot of this stuff sort of
00:35:33.720 becomes uh visible statistically once you have a large test suite and and there are things that you can get by
00:35:39.119 with when you have a small test Suite but uh you know again as with regular
00:35:44.960 code the boundary here is you don't always realize that you are you you don't realize that you are coming AC
00:35:50.280 coming up to the boundary of when you need these complexity measures until you've already passed it um you don't
00:35:57.440 know you don't realize that you need all these fancy techniques until you suddenly realize you needed them six months ago um and so you can you can
00:36:05.240 help yourself it costs a lot less to build these things in incrementally as you go um one tip that I like here is
00:36:12.680 asserting against the Primitive object um here's a active here's here's the
00:36:18.359 same search against the same test against the user um I'm creating two user objects I'm doing my search I'm
00:36:24.359 testing the search against the actual active record object um and I don't
00:36:29.839 always recommend that and I I I often write this test uh where I test against
00:36:35.119 A Primitive quote primitive value uh easy to print out value um that comes
00:36:40.960 from the object so in this case in the actual assertion uh I'm mapping the list of results to their name um and testing
00:36:47.680 against the names and the reason I do that is because it gives a much shorter more
00:36:54.720 concise error message when the test fails uh if the test fails then rspec
00:37:00.119 does not attempt to print out in this case if the test fails rspec will print out the entire active record object um
00:37:06.160 and if you have a list of these things you're trying to figure out which one isn't there that's often a pain um in this case if the test fails our speec
00:37:13.240 will just give you the the very short string um and that's much much easier to deal with uh that works for dates too uh
00:37:20.920 a test against string versions of dates instead of date versions of dates um you you will find that it is easy easier for
00:37:28.040 you to uh to uh mitigate against test failures your test failures are just be
00:37:34.119 easier to read um I also prefer this is a Jasmine
00:37:39.800 example um I like the Jasmine structure of spies not mocks if you guys don't know the difference um a spy sort of
00:37:47.079 separates into two steps what an arpec or or mocha Mach does in one step um in
00:37:52.640 Jasmine you do this in two steps uh in one step you use the spyon function to save this is a method we're looking at
00:38:00.200 uh and then later you separately uh on the last line here you separately check to see use that spy to
00:38:08.119 check and see whether the method has been called or not um and the reason in our spec you would have a single line
00:38:14.359 where you would say at the top you would say cheeseburger. cheese is uh you know
00:38:19.839 uh should receive cheeseburger. should receive cheeses um and that would stand to both be your indicator that you're
00:38:26.000 interested in this method and also uh the expectation that the method is going to be called um and the reason why I
00:38:33.240 don't like that at at a little bit of scale uh is that it it some of it's philosophical it it breaks that given
00:38:40.400 when then structure that I like because we're setting up we're specifying the data and specifying the assertion in the
00:38:46.960 same line at the beginning of the test whereas in this case we have a much cleaner line of responsibility the top
00:38:52.599 line is specifying our gathering our data including Gathering that we're spying on this method the middle line is
00:38:58.640 doing an action and the bottom line is making my testable assertion and I find that to be much easier to read and to
00:39:06.440 think about later
00:39:13.680 on um don't desert a magic literal so here's a case where we have some uh code
00:39:21.760 that's doing some math uh we're doing some this is this is actually all taken from a dayong PD Workshop that I do
00:39:28.520 internally group on um deal Deal's got a regular price it's got a sales price and
00:39:33.960 the relevant score should be 42.5 where does 42.5 come from I have no idea
00:39:39.040 looking at this test I have absolutely no idea um it actually the way that this happened most likely is that we said
00:39:44.920 that it should equal nil uh we got the test failure that said that it actually was 42.5 we plugged that value back in
00:39:50.800 the test which is a perfectly valid way to do like Legacy testing but it's not the best way to keep this going um and
00:39:58.240 this is this I don't this is the other way to do it and the other way to do it is to actually put your
00:40:04.880 math in your test and in this case this is actually putting the arithmetic in the test now uh I should say that I the
00:40:11.839 last time I gave this talk I was I went back and forth onto which one of these was actually better um since then I
00:40:17.319 actually was sort of incidentally flipping through the Kent backck test implementation patterns and Kent Back
00:40:22.920 For Better or For Worse explicitly endorses this second style um on the ground rounds that once you have the
00:40:28.200 test you can actually um take that bottom line of code and use it to build
00:40:33.560 your implementation um which is fine but I actually also think is one of the dangers of this because if you actually
00:40:40.319 copy this in your into your implementation and your math is wrong then they're both wrong in the same way
00:40:46.880 and the test won't capture it so that's so you know the if there's an advantage
00:40:51.960 of you know actually just putting the final number in the test um the advantage is just that uh it it lessens
00:41:00.240 the possibility that the test and the code will be wrong in the same way which is take it for me a very hard problem to
00:41:06.200 to to track down um also complimentary Boolean tests here this is just an
00:41:12.000 advisory uh if you test the case if you have a Boolean logic test and you test the case where it's true also write a
00:41:18.000 test for the case where it's false um just so you don't accidentally leave a tautology in there um not that I've done
00:41:25.040 that except I have um you just yeah it's just a good idea to get in that habit
00:41:30.560 when I write a test for the truth condition of something write a test for the false condition of something just as a matter of
00:41:38.079 course um when you run your tests you want to put up you want to make it as
00:41:43.160 easy as possible to run your tests and that's going to mean different things to different so for different people here for some of you that's going to mean
00:41:49.119 mapping a command key uh to your text Ed to your text editor for some of you that's going to mean being getting
00:41:54.760 really good at the command line or building some tools um to uh to run specific test Suites
00:42:02.440 internally building rake tasks or whatever um within you know the Ruby tools that most of us probably use both
00:42:08.720 rspec and cucumber have relatively straightforward mechanisms for running s individual files or individual tests or
00:42:15.920 arbitrary groups of tests um which I recommend that you take advantage of um especially if you have a long test Suite
00:42:22.440 so that you're only running the file or files that you change that you care about um rspec has mechanism where you
00:42:28.400 can only run let's see it's you only run files that have changed since your last G commit um tests that apply to those
00:42:35.400 files uh that's also handy um things like guard or auto test if you get them set up right I have kind of a LoveHate
00:42:42.240 relationship with them sometimes I really like them and sometimes they irritate me um but that's a very frictionless way to run your test is to
00:42:48.760 have them run automatically every time you save something uh if you have never used garer auto test try it for like an
00:42:55.400 hour um and and just see if you like it um also you want to display um I'm
00:43:02.079 not a huge fan of the typical arpec dot dot do dot do e do do dot do do F display I don't think that's
00:43:07.960 tremendously um uh uh valuable set of information um that said the rspec other
00:43:15.000 default the documentation default that lists all the nested uh code that's a good thing to use sometimes it's a good
00:43:21.359 way to check your naming conventions if you use that rspec nested thing you should be able to tell what all your tests are doing from that
00:43:27.760 um it actually also noticeably slows down a test Suite to use that Suite um I also if you're interested in better
00:43:33.920 things better ways to do this um there are a couple of rspec formatters one of them is called FUBAR one of them is
00:43:38.960 called f mat they both have kind of interesting slightly more useful displays uh for ours speec code over
00:43:46.040 time so I think what I want you to get in the
00:43:52.520 habit of doing is listening to your test the same way that you listen to your code like the idea of test driven
00:43:58.559 development test the thing that truly makes something test driven is that the tests are the source of Truth and if
00:44:05.920 there is a problem writing the tests um your first assumption is that that indicates a problem in the structure of
00:44:13.200 the code but I think also we needed
00:44:18.720 to um think about these sort of relatively simple ways that we can make
00:44:24.160 it easier to write and to and to read and to run these tests over time um you
00:44:29.520 know when you finish a feature when you finish a bunch of tests look back are all these tests still needed uh I was
00:44:35.920 here for Ben ornstein's refactoring talk and he said what do developers love most and everybody yelled out Delete code um
00:44:41.839 nobody loves deleting test deleting tests is scary deleting test has the risk of somebody coming over you and
00:44:48.000 going why did you delete that test um but that said and this is like handing a
00:44:53.640 very very sharp knife to people sometimes tests need to be deleted um if
00:44:59.040 you are doing a normal tdd process you will often start with some initializing structure tests that that are just there
00:45:05.319 to get your process going a lot of times those tests are duplicate with later tests in that they will never fail
00:45:11.200 independently of any other test um if you you just you need to look it is
00:45:16.720 worth taking a little bit of time to look right there when you finish the little micro feature which is when you will know as much as you will ever know
00:45:23.440 about the structure of the test and the code that you just written and look and say is this test really independent is
00:45:29.400 this test really going to if this test fails doesn't this mean that these other five tests are going to fail and if so
00:45:35.040 do I still need it again that is I understand it's dangerous I am not
00:45:40.720 saying go off and delete all your tests um I am saying though that it is possible that you have 5% 5% of your
00:45:47.880 tests you don't need or can't figure out um it's worth thinking about refactor
00:45:54.640 your tests tests are code um don't shortcut the process small
00:46:00.480 steps incremental steps the more incremental steps you take the smaller each individual step is is the more you
00:46:06.800 are mitigating the risk of doing complicated things the more you can break a complicated thing into simple
00:46:12.400 pieces the more resources you have when the actual complicated piece comes along
00:46:18.440 um uncanny avoid The Uncanny testing Valley make it your tests things that you can be confident in developer
00:46:25.680 testing all of this stuff is a means to an end the end is being able to sustainably deliver features over time
00:46:32.079 and sustainably improve your code um doing this stuff well will make it first
00:46:38.040 of all more fun to deal with your test but it will also make it easier for you to continue to sustain sustainably
00:46:44.680 improve your code so that's what I've got um I'm n
00:46:49.920 wrap on Twitter we're kind of low on time but I'll I'll hang around if people have questions I'm no RP on Twitter I
00:46:55.079 have um if you like hearing me talk you might like reading things I've written
00:47:00.359 uh I have rails test prescriptions which is a couple years old but still available from pragmatic programmers at pragprog tocom uh I also have this
00:47:07.520 JavaScript book master space and time with JavaScript uh which is self-published and you can get at
00:47:12.960 nin.com and with that that coupon code Ruby 201225 you can get a 25% off the
00:47:19.880 price of it today um relevant to this discussion it also talks a fair amount about how to use Jasmine to write good
00:47:27.079 test in a JavaScript environment um I work at group on engineering you can go to groupon.com techjobs I hope that's
00:47:33.359 the URL um and I'm no rap on Twitter and I'll be around if you guys want to say
00:47:38.400 something thank you for your time and attention I really appreciate it I hope that you got something out of this
Explore all talks recorded at RubyConf 2012
+46