Sie sind auf Seite 1von 20

(YHU\GD\ 5DLOV 7HVWLQJ ZLWK 56SHF

$ SUDFWLFDO DSSURDFK WR WHVWGULYHQ GHYHORSPHQW


zc1z Aaron Sumner
is version was published on zc1z-c-cc
is is a leanpub book, for sale at
http//leanpub.com/everydayrailsrspec
leanpub helps authors to self-publish in-progress ebooks. We call this idea lean Publishing. To
learn more about lean Publishing, go to http//leanpub.com/manifesto
To learn more about leanpub, go to http//leanpub.com
&RQWHQWV
1. Introduction 1
Who should read this book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
My testing philosophy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . z
Code conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . !
How the book is organized . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . !
3. Model specs S
Anatomy of a model spec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Creating a model spec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . e
Generating test data with factories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Testing validations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v
Testing instance methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1c
Testing class methods and scopes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Organizing specs with describe and context . . . . . . . . . . . . . . . . . . . . . . . . . 11
DRYer specs with before and aer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1z
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1e
estions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1e
lxercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
i
QWURGXFWLRQ
Ruby on Rails and automated testing go hand in hand. Rails ships with a built-in test framework; if
its not to your liking you can replace it with one of your liking (as l write this, Ruby Toolbox lists
1e projects under the Unit Test Frameworks category' alone). So yeah, testings prey important
in Railsyet many people developing in Rails are either not testing their projects at all, or at best
only adding a few token specs on model validations.
ere are several reasons for this. Perhaps working with Ruby or web frameworks is a novel enough
concept; adding an extra layer of work seems like just thatextra work. Or maybe there is a perceived
time constraintspending time on writing tests takes time away from writing the features our clients
or bosses demand. Or maybe the habit of dening test as cliing links in the browser is too hard
to break.
lve been there. lve been developing web applications since 1vv, but usually as a solo developer on
shoestring projects. Aside fromsome exposure to BASlCas a kid, a lile C-- in college, and a wasted
week of Java training in my second grown-up job outside of college, lve never had any honest-to-
goodness sooling in soware development. ln fact, it wasnt until zcc, when ld had enough of
haing ugly spaghei-style PHP code, that l sought out a beer way to write web applications.
ld looked at Ruby before, but never had a serious use for it until Rails began gaining steam. ere
was a lot to learnnew language, an actual or:ecvre, and a more object-oriented approa. lven
with all those new allenges, though, l was able to create complex applications in a fraction of the
time it took me in my previous framework-less eorts. l was hooked.
at said, early Rails books and tutorials focused more on speed (build a blog in 1 minutes!) than
on good practices like testing. lf testing was covered at all, it was generally reserved for a apter
toward the end. Newer works on Rails have addressed this shortcoming, and now demonstrate how
to test applications throughout, and a number of books have been wrien specically on the topic
of testing. But without a sound approa to the testing side, many developersespecially those in a
similar boat to the one l was inmay nd themselves without a consistent testing strategy. My goal
with this book is to introduce you to a consistent strategy that works for meone that you can then
adapt to make work consistently for you, too.
:KR VKRXOG UHDG WKLV ERRN
lf Rails is your rst foray into a web application framework, and your past programming experience
didnt involve any testing to speak of, this book will hopefully help you get started. lf youre
really new to Rails, you may nd it benecial to review coverage of testing in the likes of Miael
Hartls Ro:|: I Tvor:o| or Sam Rubys Ag:|e Ve| De+e|onen +:| Ro:|: (o| FJ::on) as wellthis
book assumes youve got some basic Rails skills under your belt. ln other words, this book wont
tea you how to use Rails, and it wont provide a ground-up introduction to the testing tools built
'https//www.ruby-toolbox.com/categories/testingframeworks
1
1. lntroduction z
into the frameworkwere going to be installing a few extras to make the testing process easier to
comprehend and manage.
lf youve been developing in Rails for a lile while, and maybe even have an application or two in
production but testing is still a foreign conceptthis book is for you! l was in your shoes for a long
time, and the teniques lll share here helped me improve my test coverage and think more like a
test-driven developer. l hope theyll do the same for you.
On the more advanced end, if youre familiar with using TestUnit, MiniTest, or even RSpec itself,
and have a workow that (a) youre comfortable with and (b) provides adequate coverage already
in place, you may be able to ne-tune some of your approa to testing your applicationsbut
to be honest, at this point youre probably on board with automated testing and dont need this
extra nudge. Other books, like David Chelimskys e RSec Boo| or Noel Rappins Ro:|: Te:
Pre:cr::on:, may be of more use to you in the long run.
0\ WHVWLQJ SKLORVRSK\
Discussing the right way to test your Rails application can invoke holy warsnot quite as bad as,
say, the Vim versus lmacs debate, but still not something to bring up in an otherwise pleasant
conversation with fellow Rubyists. Yes, there is a right way to do testingbut there are degrees of
r:g| when it comes to testing.
At the risk of starting riots among the Ruby TDD and BDD communities, my approa focuses on
the following foundation
Tests should be reliable
Tests should be easy to write
Tests should be easy to understand
lf you mind these three factors in your approa, youll go a long way toward having a sound test
suite for your applicationnot to mention becoming an honest-to-goodness practitioner of Test-
Driven Development.
Yes, there are some tradeosin particular
Were not focusing on speed
Were not focusing on overly DRY code in our tests
ln the end, though, the most important thing is that youll have testsand reliable, understandable
tests, even if theyre not quite as optimized as they could be, are a great way to start. lts the approa
that nally got me over the hump between writing a lot of application code, calling a round of
1. lntroduction !
browser-cliing testing, and hoping for the best; and taking advantage of a fully automated test
suite and using tests to drive development and ferret out potential bugs.
And thats the approa well take in this book.
&RGH FRQYHQWLRQV
Note l will be providing a download link to a complete, tested Rails application soon. Wat for
updates to the book by subscribing to updates on the books leanpub page.
lm using the following setup for this application
Rails 3.z As far as l know the teniques lm using will apply to any version of Rails from
!.c onward.
Ruby 1.v Again, the basic teniques will work; youll just need to be mindful that lll be
using the Ruby 1.v hash syntax and adjust accordingly if your application uses Ruby 1.c.
RSpec z.1o l began writing this book while version z.c was current, so anything you see here
should apply to versions of RSpec at least that far ba.
at said, this book is not a traditional tutorial. e code provided here isnt intended to walk you
through building an application; rather, its here to help you understand and learn testing paerns
and habits to apply to your own Rails applications. ln other words, you can copy and paste, but its
probably not going to do you a lot of good.
+RZ WKH ERRN LV RUJDQL]HG
F+eryJoy Ro:|: Te::ng +:| RSec is organized into the following activities
Youre reading apter 1 now.
ln apter z, well set up a new or existing Rails application to use RSpec, along with a few
extra, useful testing tools.
ln apter !, well tale testing our applications models through reliable unit testing.
Chapter 1 covers factories with a bit more depth, making test data generation straightforward.
Well look at testing controllers in apter .
http//leanpub.com/everydayrailsrspec
1. lntroduction 1
ln apter e well move on to integration testing with request specs, thus testing how the
dierent parts of our application interact with one another.
Chapter is about using specs to make sure your authentication and authorization layers are
doing their jobsthat is, keeping your apps data safe.
ln apter c well go over some teniques for refactoring your tests, making them a bit faster
and DRYer.
lll talk about what it means to practice test-driven development in apter v.
Chapter 1c wraps things up with some parting advice.
And nally, in apter 11 lll share some additional testing tools; as well as books, screencasts,
and blog posts worth reading as you continue to improve your skills in Rails testing with
RSpec.
la apter contains the step-by-step guide process l used to get beer at testing my own
soware. lollow along with the sample code and apply the teniques to your own projects. Most
apters conclude with a Q-and-A section, followed by a few exercises to follow when using these
teniques on your own. Again, l strongly recommend working through the exercises in your own
applicationswe wont be building an application together in this book, just exploring code paerns
and teniques.
Ready to go` lets start by conguring a Rails application to use RSpec.
0RGHO VSHFV
Weve got all the tools we need for building a solid, reliable test suitenow its time to put them to
work. Well get started with the apps core building blosits models.
ln this apter, well complete the following tasks
lirst well create a model spec for an existing modelin our case, the actual ooLacL model.
Next, well simplify the process of creating and maintaining test data with factories.
linally, well write passing tests for a models validations, class, and instance methods, and
organize our spec in the process.
Well create our rst spec les and factories for existing models by hand (though the handy RSpec
generators we congured in apter z can be used as templates when adding future models to our
application).
$QDWRP\ RI D PRGHO VSHF
l think its easiest to learn testing at the model level because doing so allows you to examine and
test the core building blos of an application. (An object-oriented application without objects isnt
very useful, aer all.) Well-tested code at this level is keya solid foundation is the rst step toward
a reliable overall code base.
To get started, a model spec should include tests for the following
the default factory should generate a valid object (more on factories in just a moment)
data that fail validations should not be valid
class and instance methods perform as expected
is is a good time to look at the basic structure of an RSpec model spec. l nd it helpful to think of
them as individual outlines. lor example, lets look at our main ooLacL models requirements
I ooscr1oo ooLacL
2 1L oas a va11o 1acLory
8 1L 1s 1ova11o u1LoooL a 11rsLoamo
4 1L 1s 1ova11o u1LoooL a 1asLoamo
o 1L roLoros a cooLacL`s 1o11 oamo as a sLr1oq

!. Model specs e
Well expand this outline in a few minutes, but this gives us quite a bit for starters. lts a simple spec
for an admiedly simple model, but points to our rst three best practices
Ea example (a line beginning with 1L) only expects on thing. Notice that lm testing
the 11rsLoamo and 1asLoamo validations separately. is way, if an example fails, l know its
because of that :ec:c validation, and dont have to dig through RSpecs output for clues.
Ea example is explicit. e descriptive string aer 1L is tenically optional in RSpec;
however, omiing it makes your specs more dicult to read.
Ea examples description begins with a verb, not should. S|ov|J is redundant here, and
cluers RSpecs output. Omiing it makes specs output easier to read.
With these best practices in mind, lets build a spec for the ooLacL model.
&UHDWLQJ D PRGHO VSHF
Open up the soc directory and, if necessary, create a subdirectory named mooo1s. lnside the
subdirectory create a le named cooLacL_socro and add the following
I roqo1ro `soc_oo1or`
2
8 ooscr1oo ooLacL EP
4 1L oas a va11o 1acLory
o 1L 1s 1ova11o u1LoooL a 11rsLoamo
1L 1s 1ova11o u1LoooL a 1asLoamo
7 1L roLoros a cooLacL`s 1o11 oamo as a sLr1oq
0 FOE
Well ll in the details in a moment, but if youd like you can now run the specs from your command
line
I $ rsoc soc/mooo1s/cooLacL_socro
You should see output similar to the following
I ooLacL
2 oas a va11o 1acLory (-00100 0oL yoL 1m1omooLoo)
8 1s 1ova11o u1LoooL a 11rsLoamo (-00100 0oL yoL 1m1omooLoo)
4 1s 1ova11o u1LoooL a 1asLoamo (-00100 0oL yoL 1m1omooLoo)
o roLoros a cooLacL`s 1o11 oamo as a sLr1oq (-00100 0oL yoL 1m1omooLoo)
!. Model specs

7 ooo1oq
0 ooLacL oas a va11o 1acLory
0 0oL yoL 1m1omooLoo
I0 /soc/mooo1s/cooLacL_socro4
II ooLacL 1s 1ova11o u1LoooL a 11rsLoamo
I2 0oL yoL 1m1omooLoo
I8 /soc/mooo1s/cooLacL_socroo
I4 ooLacL 1s 1ova11o u1LoooL a 1asLoamo
Io 0oL yoL 1m1omooLoo
I /soc/mooo1s/cooLacL_socro
I7 ooLacL roLoros a cooLacL`s 1o11 oamo as a sLr1oq
I0 0oL yoL 1m1omooLoo
I0 /soc/mooo1s/cooLacL_socro7
20
2I -1o1sooo 1o 00004o socooos
22 4 oxam1os, 0 1a11oros, 4 ooo1oq
Great! lour pending specslets make them pass.
As we add additional models to the contacts manager, assuming we use Rails mooo1 generator to
do so, this le (along with an associated factory) will be added automatically. (lf it doesnt go ba
and congure your applications generators now.)
*HQHUDWLQJ WHVW GDWD ZLWK IDFWRULHV
l wont spend a lot of time bad-mouthing xturesfrankly, its already been done. long story short,
there are two issues presented by xtures ld like to avoid lirst, xture data can be brile and
easily broken (meaning you spend about as mu time maintaining your test data as you do your
tests and actual code); and second, Rails bypasses Active Record when it loads xture data into your
test database. What does that mean` lt means that important things like your models validations
are ignored. is is bad!
lnter factories Simple, exible, building blos for test data. lf l had to point to a single component
that helped me see the light toward testing more than anything else, it would be lactory Girl`, an
easy-to-use and easy-to-rely-on gem for creating test data without the brileness of xtures. Since
weve got lactory Girl installed courtesy of the 1acLory_q1r1_ra11s gem we installed earlier, weve
got full access to factories in our app. lets put them to work!
Ba in the soc directory, add another subdirectory named 1acLor1os; within it, add the le
cooLacLsro with the following content
`https//github.com/thoughtbot/factorygirl
!. Model specs c
I -acLory01r1oo11oo EP
2 1acLory cooLacL EP 1
8 111rsLoamo Jooo
4 11asLoamo 0oo
o FOE
FOE
is unk of code gives us a [ocory we can use throughout our specs. lssentially, whenever we
create test data via -acLory(cooLacL), that contacts name will be John Doe. is is probably
adequate for our rst round of model specs, but l like to provide my specs with more random data.
is is where the laker gem comes into play. ldit cooLacLsro to include it
I roqo1ro `1a-or`
2
8 -acLory01r1oo11oo EP
4 1acLory cooLacL EP 1
o 111rsLoamo { -a-or0amo11rsL_oamo }
11asLoamo { -a-or0amo1asL_oamo }
7 FOE
0 FOE
Now our specs will use random, sometimes humorous names for ea generated contact. Notice
that l pass lakers 11rsL_oamo method inside a blolactory Girl considers these lazy aributes
as opposed to the statically-added strings our initial factory had.
Return to the cooLacL_socro le we set up a few minutes ago and locate the rst example (1L
oas a va11o 1acLory). Were going to write our rst specessentially testing the factory we just
created. ldit the example to look like the following
I roqo1ro `soc_oo1or`
2
8 ooscr1oo ooLacL EP
4 1L oas a va11o 1acLory EP
o -acLorycroaLo(cooLacL)sooo1o oo_va11o
FOE
7 1L 1s 1ova11o u1LoooL a 11rsLoamo
0 1L 1s 1ova11o u1LoooL a 1asLoamo
0 1L roLoros a cooLacL`s 1o11 oamo as a sLr1oq
I0 FOE
http//rubygems.org/gems/faker
!. Model specs v
is single-line spec uses RSpecs oo_va11o mater verify that our new factory does indeed return
a valid contact.
Run RSpec from the command line again and you should see one passing example, with three
pending.
7HVWLQJ YDOLGDWLRQV
Validations are a good way to break into automated testing. ese tests can usually be wrien in
just a line or two of code, especially when we leverage the convenience of factories. lets add some
detail to our 11rsLoamo validation spec
I 1L 1s 1ova11o u1LoooL a 11rsLoamo EP
2 -acLoryoo11o(cooLacL, 11rsLoamo OJM)sooo1o_ooL oo_va11o
8 FOE
Note what were doing with lactory Girl here lirst, instead of the -acLorycroaLo() approa,
were using -acLoryoo11o(). Can you guess the dierence` -acLory() builds the model and saves
it, while -acLoryoo11o() instantiates a new model, but doesnt save it. lf we used -acLory() in
this example it would break before we could even run the test, due to the validation.
Second, we use the ooLacL factorys defaults for every aribute except 11rsLoamo, and for that
we pass o11 to give it no value. ln other words, instead of the default name of }o|n Doe our ooLacL
factory would normally give us, it returns }o|n. is is an incredibly convenient feature, especially
when testing at the model level. Youll use it a lot in your testsstarting with models, but more in
other tests, too.
Run RSpec again; we should be up to two passing specs with two pending. We can use the same
approa to test the 1asLoamo validation.
I 1L 1s 1ova11o u1LoooL a 1asLoamo EP
2 -acLoryoo11o(cooLacL, 1asLoamo OJM)sooo1o_ooL oo_va11o
8 FOE
You may be thinking that these tests are relatively pointlesshow hard is it to make sure validations
are included in a model` e truth is, they can be easier to omit than you might imagine. lf you
think about what validations your model should have +|:|e writing tests (ideally, in a Test-Driven
Development paern), you are more likely to remember to include them.
ln addition, testing validations becomes more important when they are more complex than simply
validating presence or uniqueness. lor example, lets say we want to make sure we dont duplicate a
phone number for a usertheir home, oce, and mobile phones should all be unique to them. How
might you test that`
ln your oooo model spec, you might have the following example
!. Model specs 1c
I 1L ooos ooL a11ou oo11caLo oooo oomoors or cooLacL EP
2 cooLacL - -acLory(cooLacL)
8 -acLory(oooo, cooLacL cooLacL, oooo_Lyo oomo, oomoor 70ooooI2\
4 84)
o -acLoryoo11o(oooo, cooLacL cooLacL, oooo_Lyo moo11o, oomoor 70\
ooooI284)sooo1o_ooL oo_va11o
7 FOE
And make it pass with this validation in your oooo model
I va11oaLos oooo, oo1qooooss { scoo cooLacL_1o }
By the way, thats not a typo in the previous sample spec-acLory() is a shortcut for -ac
LorycroaLo().
Of course, validations can be more complicated than just requiring a specic scope. Yours might
involve a complex regular expression or a custom method. Get in the habit of testing these
validationsnot just the happy paths where everything is valid, but also error conditions.
7HVWLQJ LQVWDQFH PHWKRGV
lt would be convenient to only have to refer to cooLacLoamo to render our contacts full names
instead of creating the string every time; lets implement that feature in our ooLacL class now
I EFG oamo
2 |11rsLoamo, 1asLoamo|o1o
8 FOE
We can use the same basic teniques we used for our validation examples to create a passing
example of this feature
I 1L roLoros a cooLacL`s 1o11 oamo as a sLr1oq EP
2 cooLacL - -acLory(cooLacL, 11rsLoamo Jooo, 1asLoamo 0oo)
8 cooLacLoamosooo1o -- Jooo 0oo
4 FOE
!. Model specs 11
7HVWLQJ FODVV PHWKRGV DQG VFRSHV
lets test the ooLacL models ability to return a list of contacts whose names begin with a given
leer. lor example, if l cli S then l should get Sn:|, Svnner, and so on, but not }one:. ere are
a number of ways l could implement thisfor demonstration purposes lll show one.
lirst, lets say we oose to add this functionality via a class method like the following
I EFG TFMGoy_1oLLor(1oLLor)
2 uooro(1asLoamo .1-- ?, \1oLLor^)oroor(1asLoamo)
8 FOE
To test this, lets add the following to our ooLacL spec
I roqo1ro `soc_oo1or`
2
8 ooscr1oo ooLacL EP
4
o WBMJEBUJPO FYBNQMFT

7 1L roLoros a sorLoo array o1 roso1Ls LoaL maLco EP


0 sm1Lo - -acLory(cooLacL, 1asLoamo Sm1Lo)
0 ooos - -acLory(cooLacL, 1asLoamo Jooos)
I0 ooosoo - -acLory(cooLacL, 1asLoamo Jooosoo)
II
I2 ooLacLoy_1oLLor(J)sooo1o -- |ooosoo, ooos|
I8 FOE
I4 FOE
2UJDQL]LQJ VSHFV ZLWK GHVFULEH DQG FRQWH[W
Weve tested the happy patha user selects a name for whi we can return resultsbut what about
occasions when a selected leer returns no results` Wed beer test that, too. e following spec
should do it
I roqo1ro `soc_oo1or`
2
8 ooscr1oo ooLacL EP
4
o WBMJEBUJPO FYBNQMFT

!. Model specs 1z
7 1L roLoros a sorLoo array o1 roso1Ls LoaL maLco EP
0 sm1Lo - -acLory(cooLacL, 1asLoamo Sm1Lo)
0 ooos - -acLory(cooLacL, 1asLoamo Jooos)
I0 ooosoo - -acLory(cooLacL, 1asLoamo Jooosoo)
II
I2 ooLacLoy_1oLLor(J)sooo1o -- |ooosoo, ooos|
I8 FOE
I4
Io 1L roLoros a sorLoo array o1 roso1Ls LoaL maLco EP
I sm1Lo - -acLory(cooLacL, 1asLoamo Sm1Lo)
I7 ooos - -acLory(cooLacL, 1asLoamo Jooos)
I0 ooosoo - -acLory(cooLacL, 1asLoamo Jooosoo)
I0
20 ooLacLoy_1oLLor(J)sooo1o_ooL JODMVEF sm1Lo
2I FOE
22 FOE
is spec uses RSpecs 1oc1ooo mater to determine if the array returned by ooLacLoy_1oL
Lor(J)and it passes! Were testing not just for ideal resultsthe user selects a leer with
resultsbut also for leers with no results. However, a problem is brewing in our speccan you
spot it`
'5<HU VSHFV ZLWK EHIRUH DQG DIWHU
Our spec currently has some redundancy We create the same three objects in ea example. Just as
in your application code, the DRY principle applies to your tests (with some exceptions; see below).
lets use a few RSpec tris to clean things up.
e rst thing lm going to do is create a ooscr1oo blo +:|:n my ooscr1oo ooLacL blo to
focus on the lter feature. e general outline will look like this
I roqo1ro `soc_oo1or`
2
8 ooscr1oo ooLacL EP
4
o WBMJEBUJPO FYBNQMFT

7 ooscr1oo 111Lor 1asL oamo oy 1oLLor EP


0 GJMUFSJOH FYBNQMFT
0 FOE
I0 FOE
!. Model specs 1!
lets break things down further by including a couple of cooLoxL blosone for mating leers,
one for non-mating
I roqo1ro `soc_oo1or`
2
8 ooscr1oo ooLacL EP
4
o WBMJEBUJPO FYBNQMFT

7 ooscr1oo 111Lor 1asL oamo oy 1oLLor EP


0 cooLoxL maLco1oq 1oLLors EP
0 NBUDIJOH FYBNQMFT
I0 FOE
II
I2 cooLoxL ooomaLco1oq 1oLLors EP
I8 OPONBUDIJOH FYBNQMFT
I4 FOE
Io FOE
I FOE
While ooscr1oo and cooLoxL are tenically interangeable, l prefer to use themlike thisspecically,
ooscr1oo outlines a function of my class; cooLoxL outlines a specic state. ln my case, l have a state
of a leer with mating results selected, and a state with a non-mating leer selected.
As you may be able to spot, were creating an outline of examples here to help us sort similar
examples together. is makes for a more readable spec. lirst, lets nish cleaning up our
reorganized spec with the help of a oo1oro hook
I roqo1ro `soc_oo1or`
2
8 ooscr1oo ooLacL EP
4
o WBMJEBUJPO FYBNQMFT

7 ooscr1oo 111Lor 1asL oamo oy 1oLLor EP


0 oo1oro oaco EP
0 sm1Lo - -acLory(cooLacL, 1asLoamo Sm1Lo)
I0 ooos - -acLory(cooLacL, 1asLoamo Jooos)
II ooosoo - -acLory(cooLacL, 1asLoamo Jooosoo)
I2 FOE
I8
I4 cooLoxL maLco1oq 1oLLors EP
Io NBUDIJOH FYBNQMFT
!. Model specs 11
I FOE
I7
I0 cooLoxL ooomaLco1oq 1oLLors EP
I0 OPONBUDIJOH FYBNQMFT
20 FOE
2I FOE
22 FOE
RSpecs oo1oro hooks are vital to cleaning up unneeded redundancy from your specs. As you might
guess, the code contained within the oo1oro blo is run before eo example +:|:n |e ooscr1oo
blo. Since lve indicated that the blo should be run before eo example, RSpec will create them
for ea example individually. ln this example, my oo1oro blo will on|y be called within the
ooscr1oo 111Lor 1asL oamo oy 1oLLor bloin other words, my original validation specs will
not have access to sm1Lo, ooos, and ooosoo.
Speaking of my three mo contacts, note that since they are no longer being created within ea
example, l have to assign them to instance variables, so theyre accessible outside of the oo1oro
blo.
lf your spec requires some sort of post-example teardowndisconnecting from an external service,
sayyou can also use an a1Lor blo to clean up aer your examples. Since RSpec handles cleaning
up the database for me, l rarely use a1Lor. oo1oro, though, is indispensable.
Okay, lets see that full, organized spec
I roqo1ro `soc_oo1or`
2
8 ooscr1oo ooLacL EP
4 1L oas a va11o 1acLory EP
o -acLory(cooLacL)sooo1o oo_va11o
FOE
7
0 1L 1s 1ova11o u1LoooL a 11rsLoamo EP
0 -acLoryoo11o(cooLacL, 11rsLoamo OJM)sooo1o_ooL oo_va11o
I0 FOE
II
I2 1L 1s 1ova11o u1LoooL a 1asLoamo EP
I8 -acLoryoo11o(cooLacL, 1asLoamo OJM)sooo1o_ooL oo_va11o
I4 FOE
Io
I 1L roLoros a cooLacL`s 1o11 oamo as a sLr1oq EP
I7 -acLory(cooLacL, 11rsLoamo Jooo, 1asLoamo 0oo)oamosooo1o -- J\
I0 ooo 0oo
I0 FOE
!. Model specs 1
20
2I ooscr1oo 111Lor 1asL oamo oy 1oLLor EP
22 oo1oro oaco EP
28 sm1Lo - -acLory(cooLacL, 1asLoamo Sm1Lo)
24 ooos - -acLory(cooLacL, 1asLoamo Jooos)
2o ooosoo - -acLory(cooLacL, 1asLoamo Jooosoo)
2 FOE
27
20 cooLoxL maLco1oq 1oLLors EP
20 1L roLoros a sorLoo array o1 roso1Ls LoaL maLco EP
80 ooLacLoy_1oLLor(J)sooo1o -- |ooosoo, ooos|
8I FOE
82 FOE
88
84 cooLoxL ooomaLco1oq 1oLLors EP
8o 1L ooos ooL roLoro cooLacLs LoaL ooo`L sLarL u1Lo Loo rov1ooo 1oLLo\
8 r EP
87 ooLacLoy_1oLLor(J)sooo1o_ooL JODMVEF sm1Lo
80 FOE
80 FOE
40 FOE
4I FOE
Run the specif youve congured RSpec to use documentation format you should see a nice outline
like this
I ooLacL
2 oas a va11o 1acLory
8 1s 1ova11o u1LoooL a 11rsLoamo
4 1s 1ova11o u1LoooL a 1asLoamo
o roLoros a cooLacL`s 1o11 oamo as a sLr1oq
111Lor 1asL oamo oy 1oLLor
7 maLco1oq 1oLLors
0 roLoros a sorLoo array o1 roso1Ls LoaL maLco
0 ooomaLco1oq 1oLLors
I0 ooos ooL SFUVSO cooLacLs LoaL ooo`L sLarL u1Lo Loo rov1ooo 1oLLor
II
I2 -1o1sooo 1o 08842 socooos
I8 oxam1os, 0 1a11oros
HowDRY is too DRY' Weve spent a lot of time in this apter organizing specs into easy-to-follow
blos. like l said, oo1oro blos are key to making this happenbut theyre also easy to abuse.
!. Model specs 1e
When seing up test conditions for your example, l think its okay to bend the DRY principle in the
interest of readability. lf you nd yourself scrolling up and down a large spec le in order to see
what it is youre testing, consider duplicating your test data setup within smaller ooscr1oo blosor
even within examples themselves.
at said, well-named variables can go a long way as wellfor example, in the spec above we used
ooos and ooosoo as test contacts. ese are mu easier to follow than osorI and osor2
would have been.
6XPPDU\
And thats how l test models, but weve covered a lot of other important teniques youll want to
use in other types of specs moving forward
U:e oc:+e, e:|:c: e:eco:on:: Use verbs to explain what an examples results should be.
Only e for one result per example.
Te: [or +|o :|ov|J onJ [or +|o :|ov|J no |oen: ink about both paths when
writing examples, and test accordingly.
Orgon::e yovr :ec: [or gooJ reoJo|:|:y: Use ooscr1oo and cooLoxL to sort similar examples
into an outline format, and oo1oro and a1Lor blos to remove duplication. However, in the
case of tests readability trumps DRYif you nd yourself having to scroll up and down your
spec too mu, its okay to repeat yourself a bit.
With a solid collection of model specs incorporated into your app, youre well on your way to more
trustworthy code. Next time well apply and expand upon the teniques covered here to application
controllers.
4XHVWLRQV
:KHQ VKRXOG XVH ooscr1oo YHUVXV cooLoxL"
lrom RSpecs perspective, you can use ooscr1oo all the time, if youd like. like many other aspects
of RSpec, cooLoxL exists to make your specs more readable. You could take advantage of this to
mat a condition, as lve done in this apter, or some other state in your application.
http//lmws.net/describe-vs-context-in-rspec
!. Model specs 1
:K\ WHVW WKH IDFWRU\ LWVHOI"
Strictly speaking, this isnt necessaryand as you develop your own testing habits, you may nd it
unnecessary to verify that your models factories are yielding what theyre supposed to. However,
as you continue to use these factories in more complex tests, it can be helpful to know that a failed
test is stemming from an issue with your factory and not with something in the functionality at
hand.
([HUFLVHV
So far weve assumed our specs arent returning false positivestheyve all gone from pending to
passing without failing somewhere in the middle. Verify specs by doing the following
Comment out the code youre testing. lor example, in our example that validates the presence
of a contacts rst name, we could comment out va11oaLos 11rsLoamo, rosooco - Lroo,
run the specs, and wat 1L 1s 1ova11o u1LoooL a 11rsLoamo fail. Uncomment it to see
the spec pass again.
ldit the parameters passed to the factory within the expectation. is time, edit 1L 1s
1ova11o u1LoooL a 11rsLoamo and give 11rsLoamo a non-o11 value. e spec should fail;
replace it with o11 to see it pass again.

Das könnte Ihnen auch gefallen