Sei sulla pagina 1di 10

Rails on Maui

Programming in Paradise
RSS Email
Search

Navigate

Blog Archives Categories Tips Meta About

Capybara, PhantomJs, Poltergeist, and Rspec Tips


Mar 9th, 2014 Im a huge fan of the integration tests on Rails. Yes, they can be a bit slow, and they can be bit difficult to write and maintain, but these disadvantages far outweigh the comfort in being able to deploy code with no QA staff. This page will summarize my tips for successful integration testing on Rails. Ill try to keep this page updated with my current best setup. I look forward to your comments.

Configuration
Here are the gems Im currently using for testing. Im using an two test environments: test and ci. The reason for two is that Ive got guard constantly running specs, and sometimes I use a debugger. If guard and the debugger use the same database, then hangs occur due to transaction issues with database cleaner.

Gems
Unless the gem has a version specified, I try to keep my testing gems up to date with the latest. 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 2 2 2 3

# h t t p : / / s o u p m a t t . c o m / f i x i n g b u n d l e w i t h o u t o n h e r o k u c e d a r # H a c k t o m a k e h e r o k u w o r k . W o r k s b / c o n h e r o k u , t h e E N V [ ' H O M E ' ] w i l l b e ' a p p ' # a n d t h i s w i l l t h e n r e t u r n t e s t , w h i c h h e r o k u k n o w s t o e x c l u d e d e f h g ( g ) ( E N V [ ' H O M E ' ] . g s u b ( ' / ' , ' ' ) = = ' a p p ' ? ' t e s t ' : g ) e n d g r o u p : t e s t , h g ( : c i ) d o g e m ' s i m p l e c o v ' , r e q u i r e : f a l s e g e m ' d a t a b a s e _ c l e a n e r ' g e m ' t u r n ' , r e q u i r e : f a l s e g e m ' f a c t o r y _ g i r l _ r a i l s ' g e m ' l a u n c h y ' # t h i s l e t s u s c a l l s a v e _ a n d _ o p e n _ p a g e t o s e e w h a t ' s o n a p a g e f o r d e b u g g i n g c a p y b a r a g e m ' c a p y b a r a ' g e m ' c a p y b a r a s c r e e n s h o t ' , g i t h u b : ' p a r n d t / c a p y b a r a s c r e e n s h o t ' , b r a n c h : ' f i x r s p e c 3 0 0 d e p r e c a t i g e m ' s h o w _ m e _ t h e _ c o o k i e s ' g e m ' r s p e c i n s t a f a i l ' g e m ' s h o u l d a m a t c h e r s ' g e m ' p o l t e r g e i s t ' g e m ' r s p e c r e t r y ' , g i t h u b : ' j u s t i n 8 0 8 / r s p e c r e t r y ' , b r a n c h : ' j u s t i n 8 0 8 r s p e c 2 9 9 ' g e m ' r e s q u e _ s p e c ' , g i t h u b : ' j u s t i n 8 0 8 / r e s q u e _ s p e c ' , b r a n c h : ' f i x f o r r e s q u e h e r o k u ' g e m ' v c r '

2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3 3 4 3 5 3 6

g e m ' f a k e w e b ' g e m ' z e u s ' , ' ~ > 0 . 1 3 . 4 . p r e 2 ' e n d g r o u p : t e s t , : d e v e l o p m e n t d o # h t t p : / / r a i l s c a s t s . c o m / e p i s o d e s / 4 1 3 f a s t t e s t s g e m ' r s p e c r a i l s ' , ' ~ > 2 . 9 9 . 0 . b e t a 2 ' g e m ' r s p e c ' , ' > = 2 . 9 . 9 . b e t a 2 ' g e m ' r s p e c i t s ' , ' ~ > 1 . 0 . 0 ' g e m ' p a r a l l e l _ t e s t s ' g e m ' z e u s p a r a l l e l _ t e s t s ' g e m ' h e r o k u _ s a n ' , ' ~ > 4 . 3 . 2 ' e n d

Gemfile hosted with by GitHub

view raw

spec_helper.rb
1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3 3 4 3 5 3 6 3 7 3 8 3 9 4 0 4 1 4 2 # I M P T : # C o n f i g u r e E N V [ ' L O G _ L E V E L ' ] t o b e o n e o f D E B U G , I N F O , W A R N , E R R O R , F A T A L # f i g u r e o u t w h e r e w e a r e b e i n g l o a d e d f r o m i f $ L O A D E D _ F E A T U R E S . g r e p ( / s p e c \ / s p e c _ h e l p e r \ . r b / ) . a n y ? b e g i n r a i s e " f o o " r e s c u e = > e p u t s < < M S G = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = I t l o o k s l i k e s p e c _ h e l p e r . r b h a s b e e n l o a d e d m u l t i p l e t i m e s . N o r m a l i z e t h e r e q u i r e t o : r e q u i r e " s p e c / s p e c _ h e l p e r " T h i n g s l i k e F i l e . j o i n a n d F i l e . e x p a n d _ p a t h w i l l c a u s e i t t o b e l o a d e d m u l t i p l e t i m e s . L o a d e d t h i s t i m e f r o m : # { e . b a c k t r a c e . j o i n ( " \ n " ) } = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = M S G e n d e n d i f E N V [ " R U B Y M I N E _ H O M E " ] # t h i s i s n e c e s s a r y t o r u n t e s t s i n r u b y m i n e i n c a s e g u a r d i s r u n n i n g $ : . u n s h i f t ( F i l e . e x p a n d _ p a t h ( " r b / t e s t i n g / p a t c h / c o m m o n " , E N V [ " R U B Y M I N E _ H O M E " ] ) ) $ : . u n s h i f t ( F i l e . e x p a n d _ p a t h ( " r b / t e s t i n g / p a t c h / b d d " , E N V [ " R U B Y M I N E _ H O M E " ] ) ) e n d # i f b e l o w l i n e i s u s e d , t h e n w a r n i n g m e s s a g e s f r o m z e u s # B u t i f l i n e i s n o t u s e d a n d n o E N V i s s e t i n t h e s h e l l , t h e n f a c t o r y g i r l w o n ' t l o a d E N V [ " R A I L S _ E N V " ] | | = ' t e s t ' r e q u i r e F i l e . e x p a n d _ p a t h ( " . . / . . / c o n f i g / e n v i r o n m e n t " , _ _ F I L E _ _ ) r e q u i r e ' r s p e c / r a i l s ' r e q u i r e ' c a p y b a r a s c r e e n s h o t / r s p e c ' r e q u i r e ' r e s q u e _ s p e c / s c h e d u l e r ' # R e q u i r e s s u p p o r t i n g f i l e s w i t h c u s t o m m a t c h e r s a n d m a c r o s , e t c ,

4 3 4 4 4 5 4 6 4 7 4 8 4 9 5 0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 8 5 9 6 0 6 1 6 2 6 3 6 4 6 5 6 6 6 7 6 8 6 9 7 0 7 1 7 2 7 3 7 4 7 5 7 6 7 7 7 8 7 9 8 0

# i n . / s u p p o r t / a n d i t s s u b d i r e c t o r i e s . D i r [ R a i l s . r o o t . j o i n ( " s p e c / s u p p o r t / * * / * . r b " ) ] . e a c h { | f | r e q u i r e f } R S p e c . c o n f i g u r e d o | c o n f i g | c o n f i g . o r d e r = ' r a n d o m ' c o n f i g . m o c k _ w i t h : r s p e c c o n f i g . f i x t u r e _ p a t h = " # { : : R a i l s . r o o t } / s p e c / f i x t u r e s " # I f y o u ' r e n o t u s i n g A c t i v e R e c o r d , o r y o u ' d p r e f e r n o t t o r u n e a c h o f y o u r # e x a m p l e s w i t h i n a t r a n s a c t i o n , c o m m e n t t h e f o l l o w i n g l i n e o r a s s i g n f a l s e # i n s t e a d o f t r u e . # c o n f i g . u s e _ t r a n s a c t i o n a l _ f i x t u r e s = t r u e # h t t p : / / r a i n u x . g i t h u b . c o m / 2 0 1 1 / 0 7 / 2 3 / c o n f i g u r e c a p y b a r a w e b k i t t o r u n a c c e p t a n c e s p e c s w i t h j a v a s c r i c o n f i g . u s e _ t r a n s a c t i o n a l _ f i x t u r e s = f a l s e c o n f i g . i n c l u d e I n t e g r a t i o n S p e c H e l p e r , : t y p e = > : r e q u e s t c o n f i g . i n c l u d e D e v i s e : : T e s t H e l p e r s , : t y p e = > : c o n t r o l l e r c o n f i g . i n c l u d e C o n t r o l l e r M a c r o s , : t y p e = > : c o n t r o l l e r c o n f i g . i n c l u d e F a c t o r y G i r l : : S y n t a x : : M e t h o d s c o n f i g . i n c l u d e S h o w M e T h e C o o k i e s , : t y p e = > : f e a t u r e # V C R s e t u p # s o w e c a n u s e : v c r r a t h e r t h a n : v c r = > t r u e ; # i n R S p e c 3 t h i s w i l l n o l o n g e r b e n e c e s s a r y . c o n f i g . t r e a t _ s y m b o l s _ a s _ m e t a d a t a _ k e y s _ w i t h _ t r u e _ v a l u e s = t r u e c o n f i g . i n c l u d e ( M a i l e r M a c r o s ) c o n f i g . b e f o r e ( : e a c h ) { r e s e t _ e m a i l } # h t t p : / / r a i l s c a s t s . c o m / e p i s o d e s / 4 1 3 f a s t t e s t s c o n f i g . f i l t e r _ r u n : f o c u s u n l e s s E N V [ " S K I P _ R S P E C _ F O C U S " ] . p r e s e n t ? c o n f i g . r u n _ a l l _ w h e n _ e v e r y t h i n g _ f i l t e r e d = t r u e c o n f i g . b e f o r e ( : a l l ) { D e f e r r e d G a r b a g e C o l l e c t i o n . s t a r t } c o n f i g . a f t e r ( : a l l ) { D e f e r r e d G a r b a g e C o l l e c t i o n . r e c o n s i d e r } c o n f i g . m o c k _ w i t h : r s p e c d o | c | c . y i e l d _ r e c e i v e r _ t o _ a n y _ i n s t a n c e _ i m p l e m e n t a t i o n _ b l o c k s = t r u e e n d e n d

spec_helper.rb hosted with by GitHub

view raw

.rspec file
This file configures the defaults for running tests. I really like the instalfail gem so that I can see errors immediately.
c o l o r r e q u i r es p e c _ h e l p e r r e q u i r er s p e c / i n s t a f a i l f o r m a tR S p e c : : I n s t a f a i l b a c k t r a c e f o r m a td o c u m e n t a t i o n

Using :focus with guard-rspec


My favorite way to run specs is using Zeus, Guard, and finally with parallel_tests. Heres the part of my Guardfile for my s p e cgroup: 1 2 3 4 g r o u p : s p e c d o g u a r d ( ' r s p e c ' , a l l _ o n _ s t a r t : f a l s e , a l l _ a f t e r _ p a s s : f a l s e , r u n _ a l l : { p a r a l l e l : t r u e , p a r a l l e l _ c l i : ' n 4 ' } ,

5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1

c m d : ' z e u s r s p e c ' , n o t i f i c a t i o n : t r u e , f a i l e d _ m o d e : : k e e p ) d o w a t c h ( % r { ^ s p e c / . + _ s p e c \ . r b $ } ) w a t c h ( % r { ^ l i b / ( . + ) \ . r b $ } ) { | m | " s p e c / l i b / # { m [ 1 ] } _ s p e c . r b " } w a t c h ( " s p e c / s p e c _ h e l p e r . r b " ) { " s p e c " } w a t c h ( % r { ^ a p p / ( . + ) \ . r b $ } ) { | m | " s p e c / # { m [ 1 ] } _ s p e c . r b " } w a t c h ( % r { ^ a p p / ( . * ) ( \ . e r b | \ . s l i m | \ . h a m l ) $ } ) { | m | " s p e c / # { m [ 1 ] } # { m [ 2 ] } _ s p e c . r b " } w a t c h ( % r { ^ a p p / c o n t r o l l e r s / ( . + ) _ ( c o n t r o l l e r ) \ . r b $ } ) { | m | [ " s p e c / r o u t i n g / # { m [ 1 ] } _ r o u t i n g _ s p e c . r b " w a t c h ( % r { ^ s p e c / s u p p o r t / ( . + ) \ . r b $ } ) { " s p e c " } w a t c h ( " c o n f i g / r o u t e s . r b " ) { " s p e c / r o u t i n g " } w a t c h ( " a p p / c o n t r o l l e r s / a p p l i c a t i o n _ c o n t r o l l e r . r b " ) { " s p e c / c o n t r o l l e r s " } w a t c h ( % r { ^ a p p / v i e w s / ( . + ) / . * \ . ( e r b | s l i m | h a m l ) $ } ) { | m | " s p e c / f e a t u r e s / # { m [ 1 ] } _ s p e c . r b " } e n d e n d

Guardfile hosted with by GitHub

view raw

Heres my workflow for refining a test, and then running all tests in parallel: 1. Flag test (either d e s c r i b e ,c o n t e x t ,i t ,f e a t u r e , or s c e n a r i o ) with : f o c u slike this:
1f e a t u r e" U s e r s " ,: j s ,: f o c u sd o 2s c e n a r i o" s i g n u pf a i l u r es h o u l dn o tm a k ean e wu s e r " ,: f o c u sd o

2. In guard console for your specs, run command afor all tests (or just hit return). That will run only the tests youve marked with : f o c u s . 3. Sometimes guard picks up the changes and re-runs your test automatically. Many times, I just cmd-tab to iterm2 and hit return, which runs the default command of all specs. 4. Be sure to search and replace for , :focus so that you can run the whole test suite. If you forget to remove a :focus, then youll see that the total number of tests is less than you expect. 5. Once your new tests pass, run the whole test suite in parallel with the command z p s , defined below.

Debugging Integration (aka Feature) Tests


1. 2. 3. 4. First, try to manually do test actions in the browser. Test out your CSS and xpath selectors in the chrome console. a. $ ( " c s ss e l e c t o r " )b. $ x ( " x p a t h s e l e c t o r " ) Call p a g e !in your test right before the error and a browser will open up with the page contents. Call render_page(some_name, true) to print out a screenshot. Usually, either the h t m lor the p n gversion of a page will tell you where your integration test is losing the plot.

Heres a couple convenient helper methods, page! and render_page:


1 d e fp a g e ! 2 s a v e _ a n d _ o p e n _ p a g e 3 e n d 4 5 #S a v e sp a g et op l a c es p e c f i e da ti nc o n f i g u r a t i o n . 6 #N O T E :y o um u s tp a s sj s :t r u ef o rt h ef e a t u r ed e f i n i t i o n( o re l s ey o u ' l ls e et h a tr e n d e rd o e s n ' te x i s t ! ) 7 #c a l lf o r c e=t r u e ,o rs e tE N V [ R E N D E R _ S C R E E N S H O T S ]= =' Y E S ' 8 d e fr e n d e r _ p a g e ( n a m e ,f o r c e=f a l s e ) 9 i ff o r c e| |( E N V [ ' R E N D E R _ S C R E E N S H O T S ' ]= =' Y E S ' ) 1 0 p a t h=F i l e . j o i nR a i l s . a p p l i c a t i o n . c o n f i g . i n t e g r a t i o n _ t e s t _ r e n d e r _ d i r ," # { n a m e } . p n g " 1 1 p a g e . d r i v e r . r e n d e r ( p a t h ) 1 2 e n d 1 3e n d

Xpath
Sometimes a Capybara test requires an xpath for more advanced finding of just the right dom node. For example, heres a useful xpath snippet for finding the xpath to the page one link.

1p a g e _ 1=f i n d ( : x p a t h ,' / / d i v [ c o n t a i n s ( @ c l a s s , " p a g i n a t i o n " ) ] / / a [ n o r m a l i z e s p a c e ( . ) = " 1 " ] ' )

If you use a context (within), then be sure to use .// and not / as / means anywhere on the page, not just in the current context! Another case where I had to use an xpath was to find the parent of a node. While the Capybara node object has a method p a r e n t , that does not give you the same sort of dome node parent that jQuery would. Thus, you can use an xpath like this, which finds an anchor with attribute d a t a s o m e t h i n ghaving value in ruby variable d a t a _ v a l u e .
1t h e _ n o d e=f i n d ( : x p a t h ," / / a [ @ d a t a s o m e t h i n g = ' # { d a t a _ v a l u e } ' ] / . . " )

Debugger vs. Print Statements


80% of the time, I use print statements for debugging, rather than the awesome RubyMine debugger. Here are the pros and cons of each:

Print Statements
1. Very fast to see exactly the data you need. 2. No issues with multiple threads when running integration tests. 3. Print statements help with the coffeescript code. Just do a:
1c o n s o l e . l o g" s o m em e s s a g e ,m y _ v a r# { m y _ v a r } "

4. With Zeus running, its much faster to re-run a test and get the print statements rather than starting the RubyMine debugger.

Debugger
1. For tough problems, the debugger can really help. 2. Allows you to evaluate code and dig into variables. 3. Ill tend to use the RubyMine debugger more for running the Rails server, since theres no waiting for the process to start if the debugger is already running the server. 4. Its key to set breakpoints where you think youll need them, start your test, and then move your cursor to some point where the problem is manifesting. The hit menu choice Run -> Force Run to Cursor. That is a HUGE time saver.

Database Cleaner
Its pretty critical to use Database Cleaner correctly when using Capybara with Poltergeist. Heres my setup. The only thing specific is that I have a couple tables that are seeded when the database is created. 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 R S p e c . c o n f i g u r e d o | c o n f i g | c o n f i g . a d d _ s e t t i n g ( : s e e d _ t a b l e s ) c o n f i g . s e e d _ t a b l e s = % w ( s e e d _ t a b l e _ 1 s e e d _ t a b l e _ 2 ) c o n f i g . b e f o r e ( : s u i t e ) d o D a t a b a s e C l e a n e r . c l e a n _ w i t h ( : t r u n c a t i o n , e x c e p t : c o n f i g . s e e d _ t a b l e s ) e n d c o n f i g . b e f o r e ( : e a c h ) d o D a t a b a s e C l e a n e r . s t r a t e g y = : t r a n s a c t i o n e n d c o n f i g . b e f o r e ( : e a c h , j s : t r u e ) d o D a t a b a s e C l e a n e r . s t r a t e g y = : t r u n c a t i o n , { e x c e p t : c o n f i g . s e e d _ t a b l e s } e n d c o n f i g . b e f o r e ( : e a c h ) d o D a t a b a s e C l e a n e r . s t a r t e n d c o n f i g . a f t e r ( : e a c h ) d o

2 2 D a t a b a s e C l e a n e r . c l e a n 2 3 e n d 2 4 e n d
database_cleaner.rb hosted with by GitHub view raw

Tricky Testing
AJAX
This is well documented on the Capybara website. Read the part about AJAX very closely. The key thing is to think of what will change on the page once your AJAX response comes back. Then use a statement like:
1e x p e c t ( p a g e ) . t oh a v e _ c o n t e n t ( " s o m ev a l u e " )

Capybara will be smart about waiting until that condition is true. However, you have to be clever to come up with just the right condition. Be sure to understand how e x p e c t ( p a g e ) . t oh a v e _ c o n t e n t ( " b l a h " )will poll the page until the timeout or blah appears. On the contrary, e x p e c t ( p a g e ) . t o _ n o th a v e _ c o n t e n tc o n t a i n ( " b l a h " )will not poll! So be sure to use a positive expectation after you take some action invoking an AJAX request (or even an animation). Or else you may get a false positive that something is not on the page just because the page has not finished loading.

Auto-complete dropdowns with Capybara and Poltergeist


Im using typeahead.js with Twitter Bootstrap 3, both of which rock! Heres the secret sauce for doing capybara feature tests with the typeahead.js auto-complete. This technique should work for other types of auto-complete as well. To make this work for your code, modify the . t t s u g g e s t i o nselector depending on how you choose to render drop downs. 1 2 3 4 5 6 7 8 9 1 0 d e f f i l l _ i n _ a u t o c o m p l e t e ( s e l e c t o r , v a l u e ) s c r i p t = % Q { $ ( ' # { s e l e c t o r } ' ) . v a l ( ' # { v a l u e } ' ) . f o c u s ( ) . k e y p r e s s ( ) } p a g e . e x e c u t e _ s c r i p t ( s c r i p t ) e n d d e f c h o o s e _ a u t o c o m p l e t e ( t e x t ) e x p e c t ( p a g e ) . t o h a v e _ c s s ( " . t t s u g g e s t i o n p " , t e x t : t e x t , v i s i b l e : f a l s e ) s c r i p t = % Q { $ ( ' . t t s u g g e s t i o n : c o n t a i n s ( " # { t e x t } " ) ' ) . c l i c k ( ) } p a g e . e x e c u t e _ s c r i p t ( s c r i p t ) e n d
view raw

typeahead_helper.rb hosted with by GitHub

Heres the most relevant links on this topic: 1. How to Do jQuery UI Autocomplete With Capybara 2 2. Poltergeist github issues: 439, 274, 43

Hover effects
Heres how you test a mouseover effect. This was recently fixed for Capybara and Poltergeist and this absolutely rocks!
1s o m e _ n o d e=f i n d ( " c s ss e l e c t o r " ) 2s o m e _ n o d e . h o v e r 3e x p e c t ( p a g e ) . t oh a v e _ s e l e c t o r ( " s o m eo t h e rc s s _ s e l e c t o r " )

For fun, you can put some console.log in your event handler, and run the tests, and see in your console output that Capybara triggers the event.

Unreliable Tests: rspec-retry Gem Handles Intermittent Phantomjs Issues

Its not perfect that I have to use rspec-retry to get past random failures with PhantomJs. However, its way better than to let rspec simply retry before considering the test a failure. Note, you dont to have the : r e t r y _ c o u n tat anything other than 1 when youre re-running a test while developing it, as that would really slow you down.
1# D i s c u s s i o no fr e t r y 2# h t t p s : / / g i t h u b . c o m / r s p e c / r s p e c c o r e / i s s u e s / 4 5 6 3R S p e c . c o n f i g u r ed o| c o n f i g | 4 c o n f i g . v e r b o s e _ r e t r y =t r u e#s h o wr e t r ys t a t u si ns p e cp r o c e s s 5 r e t r y _ c o u n t =E N V [ ' R S P E C _ R E T R Y _ C O U N T ' ] 6 c o n f i g . d e f a u l t _ r e t r y _ c o u n t=r e t r y _ c o u n t . t r y ( : t o _ i )| |1 7 p u t s" R S p e cr e t r yc o u n ti s# { c o n f i g . d e f a u l t _ r e t r y _ c o u n t } " 8e n d

Testing with Zeus


Overall, Im quite pleased with the performance boost of Zeus, especially for rake tasks, running specs, and running specs in parallel (stunning difference). Its super important to remember that you have to restart Zeus whenever you want files like test.rb or spec_helper.rb (and maybe factories.rb or Gemfile) to be re-evaluated. This can cause some very confusing results until you restart Zeus. Heres my z e u s . j s o nfile, configured to work with parallel-tests.
1 { 2 " c o m m a n d " :" r u b yr u b y g e m sr . / c u s t o m _ p l a ne Z e u s . g o " , 3 4 " p l a n " :{ 5 " b o o t " :{ 6 " d e f a u l t _ b u n d l e " :{ 7 " d e v e l o p m e n t _ e n v i r o n m e n t " :{ 8 " p r e r a k e " :{ 9 " r a k e " :[ ] 1 0 } , 1 1 " r u n n e r " :[ " r " ] , 1 2 " c o n s o l e " :[ " c " ] , 1 3 " s e r v e r " :[ " s " ] , 1 4 " g e n e r a t e " :[ " g " ] , 1 5 " d e s t r o y " :[ " d " ] , 1 6 " d b c o n s o l e " :[ " d b " ] , 1 7 " p a r a l l e l _ r s p e c " :[ ] 1 8 } , 1 9 " t e s t _ e n v i r o n m e n t " :{ 2 0 " t e s t _ h e l p e r " :{ 2 1 " t e s t " :[ " r s p e c " ] , 2 2 " p a r a l l e l _ r s p e c _ w o r k e r " :[ ] 2 3 } 2 4 } 2 5 } 2 6 } 2 7 } 2 8}

Every once in a while, my setup borks, and I need to kill all my zeus, guard, phantomjs processes at once. Heres a couple useful zsh functions:
1 p g r ( ){ 2 f o rxi nr a i l sp h a n t o m j sz e u s ;d o 3 p g r e pf l$ x ; 4 d o n e 5 } 6 7 p g k ( ){ 8 f o rxi nr a i l sp h a n t o m j sz e u s ;d o 9 p k i l lf l$ x ; 1 0 d o n e 1 1}

Parallel Tests

As mentioned above, I got a stunning performance difference using the parallel_tests gem. However, it defaults to using as many threads as one has processors (8 on my Macbook). Since that is too many if you want to keep using your Mac, I use this zsh function, which defaults to 6 processors.
1z p s( ){ 2 p = $ { 1 : 6 } 3 e c h o R u n" z e u sp a r a l l e l _ r s p e cn$ ps p e c " 4}

The trickiest part of the parallel_tests gem is setting up and migrating the extra test databases. Heres my rake task for doing migrations, which updates test, development, and my parallel test databases, and annotates. 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3 3 4 3 5 3 6 3 7 3 8 3 9 4 0 d e f r u n _ c o m m a n d _ r a i s e _ i f _ e r r o r ( c o m m a n d ) p u t s " E x e c u t i n g : # { c o m m a n d } " r e s u l t = % x [ # { c o m m a n d } ] r a i s e " r a k e t a s k f a i l e d . . . . . . . . . . \ n # { r e s u l t } " i f r e s u l t . i n c l u d e ? ( ' r a k e a b o r t e d ! ' ) e n d d e f r u n _ r a k e _ c o m m a n d _ r a i s e _ i f _ e r r o r ( c o m m a n d ) r u n _ c o m m a n d _ r a i s e _ i f _ e r r o r ( " b u n d l e e x e c r a k e # { c o m m a n d } " ) e n d n a m e s p a c e : d b d o d e s c " M i g r a t e D B s f o r e n v s t e s t , c i , d e v e l o p m e n t . T h e n s e t u p p a r a l l e l . " t a s k m i g r a t e _ a l l : : e n v i r o n m e n t d o | t , a r g s | u n l e s s R a i l s . e n v . d e v e l o p m e n t ? r a i s e " C a n o n l y r u n u n d e r d e v e l o p m e n t e n v i r o n m e n t , b u t s p e c i f i e d e n v w a s # { R a i l s . e n v } " e n d e n v s = % W ( t e s t c i d e v e l o p m e n t ) e n v s . e a c h d o | e n v | r u n _ r a k e _ c o m m a n d _ r a i s e _ i f _ e r r o r ( " R A I L S _ E N V = # { e n v } d b : m i g r a t e " ) e n d r u n _ r a k e _ c o m m a n d _ r a i s e _ i f _ e r r o r ( " p a r a l l e l : c r e a t e " ) r u n _ r a k e _ c o m m a n d _ r a i s e _ i f _ e r r o r ( " p a r a l l e l : p r e p a r e " ) r u n _ r a k e _ c o m m a n d _ r a i s e _ i f _ e r r o r ( " p a r a l l e l : r a k e [ d b : g l o b a l s ] " ) r u n _ c o m m a n d _ r a i s e _ i f _ e r r o r ( " a n n o t a t e e \ [ t e s t s \ ] i " ) e n d d e s c " U p d a t e s e e d d a t a i n a l l t e s t / d e v d a t a b a s e s " t a s k s e e d _ a l l : : e n v i r o n m e n t d o | t , a r g s | u n l e s s R a i l s . e n v . d e v e l o p m e n t ? r a i s e " C a n o n l y r u n u n d e r d e v e l o p m e n t e n v i r o n m e n t , b u t s p e c i f i e d e n v w a s # { R a i l s . e n v } " e n d e n v s = % W ( t e s t c i d e v e l o p m e n t ) e n v s . e a c h d o | e n v | r u n _ r a k e _ c o m m a n d _ r a i s e _ i f _ e r r o r ( " R A I L S _ E N V = # { e n v } d b : s e e d " ) e n d r u n _ r a k e _ c o m m a n d _ r a i s e _ i f _ e r r o r ( " p a r a l l e l : r a k e [ d b : s e e d ] " ) e n d e n d
view raw

parallel_tests.rake hosted with by GitHub

Mar 9th, 2014


Tweet 1 1 Like Share 3 people like this. Be the first of your friends.

Comments

2 Comments
Sort by Oldest

Rails On Maui
Share

Login
Favorite

Join the discussion


aaronmcadam

4 days ago

Thanks for all the detail! Great post, would like to see more blog posts like this that go into more detail on developer toolchains.
Reply Share

joneslee85

3 days ago

Great post mate, thanks for pointing out the hover API
Reply Share

ALSO ON RAILS ON MAUI

WHAT'S THIS?

Ember.js Hello World with Rails 4 Persistence


2 comments 9 months ago

Favorite RubyMine Tips


1 comment 10 months ago

Justin Gordon Hi Aaron, Thanks for the kind words. I have

Ryan Buckley Very nice coverage of all the shortcuts. I use

not yet looked into the issue you mention, but that's a common issue for any

almost all those on a daily basis. My favorites being (CMD + Shft + N) to search for a

CoffeeScript Chrome Extensions


1 comment 10 months ago

GoGaRuCo 2013: Community > Code


2 comments 6 months ago

wcanyon Nice roundup, it was helpful.

Justin Gordon I definitely will! BTW, GREAT JOB with the

conference organization! @maximilien and I might organize a smaller Ruby conf in Maui next

Subscribe

Add Disqus to your site

About Me
I'm Justin Gordon. I'm a Ruby on Rails consultant based in Maui. Full stack development is my passion. Feel free to contact me on gmail as j u s t i n . g o r d o n . I'm also on Linked In. Resume.
Follow @railsonmaui 245 followers

Recent Posts
Rocking With Tmux, Tmuxinator, Guard, Zeus, and iTerm2 for Rails Development Capybara PhantomJs Poltergeist Rspec Tips Org-Mode Octopress Setup V2 Simple Form and Disabling Buttons on Submit by Default Using RubyMine/IntelliJ Regexp Search/Replace Migrating From Bash to Zsh GoGaRuCo 2013: Community > Code

GitHub Repos
justin808.github.io Rails On Maui Octopress org-mode

fork of official org-mode repo angularjs-railsonmaui-tutorial Angular spin on EmberJs Tutorial ember-js-guides-railsonmaui-rails4 Ember.js Example with Rails 4 Backend dof Dependent Object Framework JUnit ember-js-guides-railsonmaui-no-rest Completed set of files for the Tom Dale Ember.js Tutorial. ember-js-guides-railsonmaui-start Starter set of files for the Tom Dale Ember.js Tutorial. @justin808 on GitHub

Google+
Copyright 2014 - Justin Gordon - Powered by Octopress

Potrebbero piacerti anche