A proposal for a workflow for developing AMD modules with a mockist (London School TDD) approach, in order help the developer avoid complexity and stay focused on the module under development rather than its collaborators.
f u n c t i o n ( ) { v a r c o l o r s = $ ( t h i s ) . d a t a ( ' c o l o r s ' ) ; v a r $ g r i d E l e m e n t = $ ( ' < d i v > < / d i v > ' ) . a d d C l a s s ( ' g r i d ' ) ; f o r ( v a r i = 0 ; i < _ g r i d S i z e ; i + + ) { v a r c e l l C l a s s = ( t y p e o f c o l o r s [ i ] = = = ' u n d e f i n e d ' ) ? _ d e f a u l t C l a s s : c l a s s N a m e ; v a r c e l l E l e m e n t = $ ( ' < d i v > < / d i v > ' ) . a d d C l a s s ( c e l l C l a s s ) ; i f ( _ g r i d S i z e % 2 = = 0 ) { c e l l C l a s s = ' g r e e n ' ; } e l s e i f ( _ g r i d S i z e % 3 = = 0 ) { / / e v e r y t h i r d o n e n e e d s a d i f f e r e n t c o l o r f o r ( v a r j = 1 ; j < _ g r i d S i z e / 2 ; j + + ) { i f ( c o l o r s [ 0 ] ) { c o l o r s [ 0 ] = n u l l ; } e l s e { c o l o r s [ 0 ] = c o l o r s [ 1 ] } } i f ( c o l o r s . l e n g t h > 4 ) { / / r e m o v e o n e c e l l $ g r i d E l e m e n t . f i r s t ( ) . r e m o v e ( ) ; } } $ g r i d E l e m e n t . a p p e n d ( c e l l E l e m e n t ) ; }
v a r A c m e = ( f u n c t i o n ( A c m e ) { A c m e . N a m e s p a c e = A c m e . N a m e s p a c e | | { } ; A c m e . N a m e s p a c e . M o d u l e = f u n c t i o n ( ) { / / c o n s t r u c t o r v a r p r i v a t e V a r i a b l e ; r e t u r n { m e t h o d O n e : f u n c t i o n ( ) { } , m e t h o d T w o : f u n c t i o n ( ) { } } } r e t u r n A c m e ; } ) ( A c m e | | { } ) ;
r c = " p a t h / a c m e / d e p e n d e n c i e s . j s " > < / s c r i p t > < s c r i p t s r c = " p a t h / v e n d o r / c o n f i g . j s " > < / s c r i p t > < s c r i p t s r c = " p a t h / a c m e / m o d u l e 1 . j s " > < / s c r i p t > < s c r i p t s r c = " p a t h / v e n d o r / m o d u l e 2 . j s " > < / s c r i p t > < s c r i p t s r c = " p a t h / j s / m o d u l e 4 . j s " > < / s c r i p t > < s c r i p t s r c = " p a t h / j s / m o d u l e 5 . j s " > < / s c r i p t > < ? i f ( $ e n v = = = ' l i v e ' ) : ? > < s c r i p t s r c = " p a t h / j s / m o d u l e 6 . j s " > < / s c r i p t > < ? e n d i f ? >
JS modules asynchronously https://github.com/amdjs/amdjs-api/wiki/AMD Provides a mechanism for managing dependencies Provides an isolated context for each module
m o d u l e d e f i n e ( f u n c t i o n ( ) f u n c t i o n _ v a l i d a t e ( a r g s ) { i f ( a r g s . l e n g t h ! = = 2 ) { t h r o w ' M i s s i n g a r g u m e n t ' ; } } r e t u r n { m e t h o d O n e : f u n c t i o n ( a r g 1 , a r g 2 ) { _ v a l i d a t e ( a r g u m e n t s ) ; } } } ) ;
y m o d u l e - > d e p e n d s o n : c a l c u l a t o r d e f i n e ( [ ' c a l c u l a t o r ' ] , f u n c t i o n ( c a l c u l a t o r ) { f u n c t i o n _ v a l i d a t e ( a r g s ) { i f ( a r g s . l e n g t h ! = = 2 ) { t h r o w ' M i s s i n g a r g u m e n t ' ; } } r e t u r n { m e t h o d O n e : f u n c t i o n ( a r g 1 , a r g 2 ) { _ v a l i d a t e ( a r g u m e n t s ) ; r e t u r n c a l c u l a t o r . c a l c u l a t e ( a r g 1 , a r g 2 ) ; } } } ) ;
m y m o d u l e - > d e p e n d s o n : c a l c u l a t o r a n d f o r m a t t e r / h t m l d e f i n e ( [ ' c a l c u l a t o r ' , ' f o r m a t t e r / h t m l ' ] , f u n c t i o n ( c a l c u l a t o r , f o r m a t t e r ) { f u n c t i o n _ v a l i d a t e ( a r g s ) { i f ( a r g s . l e n g t h ! = = 2 ) { t h r o w ' M i s s i n g a r g u m e n t ' ; } } r e t u r n { m e t h o d O n e : f u n c t i o n ( a r g 1 , a r g 2 ) { _ v a l i d a t e ( a r g u m e n t s ) ; v a r r e s u l t = c a l c u l a t o r . c a l c u l a t e ( a r g 1 , a r g 2 ) ; r e t u r n f o r m a t t e r . f o r m a t ( r e s u l t ) ; } } } ) ;
. j s o n { " n a m e " : " m o c k i s t - t d d " , " d e v D e p e n d e n c i e s " : { " g r u n t " : " ^ 0 . 4 . 5 " , " g r u n t - c o n t r i b - j a s m i n e " : " ^ 0 . 6 . 5 " , " g r u n t - t e m p l a t e - j a s m i n e - r e q u i r e j s " : " ^ 0 . 2 . 0 " } }
to completely decouple the modules from each other. Ergo, we need a simple mechanism for swapping out modules depending on environment. Ergo, we need a dependency injection container!
expected Prefers to check resulting state and data Doesn't care if more than one object is involved in the test Prefers not to mock as it is often complicated WHAT IS THE MOCKIST APPROACH Emphasis on roles and responsibilities of objects Mocks all collaborators Outside-in approach to development Characterized by the "Tell, don't ask" rule of OO design
s p e c / p r o d u c t O p t i o n s C o n t r o l s . j s d e f i n e ( [ ' j q u e r y ' , ' p r o d u c t O p t i o n C o n t r o l s ' ] , f u n c t i o n ( $ , p r o d u c t O p t i o n C o n t r o l s ) { d e s c r i b e ( ' P r o d u c t O p t i o n s C o n t r o l s P l u g i n ' , f u n c t i o n ( ) { v a r e l e m e n t ; i t ( ' R e b u i l d s a n e m p t y c o l o u r g r i d g i v e n n o d a t a ' , f u n c t i o n ( ) { e l e m e n t . a t t r ( ' d a t a - c o l o r s ' , ' [ ] ' ) ; p r o d u c t O p t i o n C o n t r o l s . m e t h o d s . r e b u i l d . a p p l y ( e l e m e n t ) ; e x p e c t ( e l e m e n t . h t m l ( ) ) . t o B e ( ' < d i v c l a s s = " g r i d " > ' + ' < d i v c l a s s = " e m p t y " > < / d i v > ' + ' < d i v c l a s s = " e m p t y " > < / d i v > ' + ' < d i v c l a s s = " e m p t y " > < / d i v > ' + ' < d i v c l a s s = " e m p t y " > < / d i v > ' + ' < / d i v > ' ) ; } ) ; b e f o r e E a c h ( f u n c t i o n ( ) { e l e m e n t = $ ( ' < d i v > < / d i v > ' ) . a t t r ( { ' i d ' : ' m a i n ' , ' d a t a - c o l o r s ' : ' ' } ) ; $ ( d o c u m e n t . b o d y ) . a p p e n d ( e l e m e n t ) ; } ) ;
p r o d u c t C o n t r o l s O p t i o n s . j s d e f i n e ( [ ' j q u e r y ' ] , f u n c t i o n ( $ ) { v a r _ g r i d S i z e = 4 ; v a r _ d e f a u l t C l a s s = ' e m p t y ' ; v a r _ m e t h o d s = { / * * * R e b u i l d t h e c o l o u r g r i d * / r e b u i l d : f u n c t i o n ( ) { v a r c o l o r s = $ ( t h i s ) . d a t a ( ' c o l o r s ' ) ; v a r g r i d E l e m e n t = $ ( ' < d i v > < / d i v > ' ) . a d d C l a s s ( ' g r i d ' ) ; f o r ( v a r i = 0 ; i < _ g r i d S i z e ; i + + ) { v a r c e l l C l a s s = ( t y p e o f c o l o r s [ i ] = = = ' u n d e f i n e d ' ) ? _ d e f a u l t C l a s s : c l a s s N a m v a r c e l l E l e m e n t = $ ( ' < d i v > < / d i v > ' ) . a d d C l a s s ( c e l l C l a s s ) ; g r i d E l e m e n t . a p p e n d ( c e l l E l e m e n t ) ; } $ ( t h i s ) . a p p e n d ( g r i d E l e m e n t ) ; } } ;
o d u c t C o n t r o l s O p t i o n s . j s d e f i n e ( [ ' j q u e r y ' , ' g r i d B u i l d e r ' ] , f u n c t i o n ( $ , g r i d B u i l d e r ) { v a r _ g r i d S i z e = 4 ; v a r _ m e t h o d s = { / * * * R e b u i l d t h e c o l o u r g r i d * / r e b u i l d : f u n c t i o n ( ) { v a r c o l o r s = $ ( t h i s ) . d a t a ( ' c o l o r s ' ) ; v a r g r i d = g r i d B u i l d e r . b u i l d ( _ g r i d S i z e , c o l o r s ) ; $ ( t h i s ) . a p p e n d ( g r i d ) ; } } ; r e t u r n { m e t h o d s : _ m e t h o d s } } ) ;
r c / g r i d B u i l d e r . j s d e f i n e ( [ ' j q u e r y ' ] , f u n c t i o n ( $ ) { v a r d e f a u l t C l a s s = ' e m p t y ' ; v a r _ c e l l B u i l d e r = f u n c t i o n ( c l a s s N a m e ) { v a r c e l l C l a s s = ( t y p e o f c l a s s N a m e = = = ' u n d e f i n e d ' ) ? d e f a u l t C l a s s : c l a s s N a m e ; r e t u r n $ ( ' < d i v > < / d i v > ' ) . a d d C l a s s ( c e l l C l a s s ) ; } ; r e t u r n { b u i l d : f u n c t i o n ( s i z e , c o l o r s ) { v a r g r i d E l e m e n t = $ ( ' < d i v > < / d i v > ' ) . a d d C l a s s ( ' g r i d ' ) ; f o r ( v a r i = 0 ; i < s i z e ; i + + ) { g r i d E l e m e n t . a p p e n d ( _ c e l l B u i l d e r ( c o l o r s [ i ] ) ) ; } r e t u r n g r i d E l e m e n t ; } } } ) ;
d u c t O p t i o n s C o n t r o l s . j s d e f i n e ( [ ' j q u e r y ' ] , f u n c t i o n ( $ ) { v a r _ g r i d S i z e = 4 ; v a r _ m e t h o d s = { / * * * R e b u i l d t h e c o l o u r g r i d * / r e b u i l d : f u n c t i o n ( ) { v a r c o l o r s = $ ( t h i s ) . d a t a ( ' c o l o r s ' ) ; / / a t t h i s p o i n t w e r e a l i z e w e / / n e e d t o b u i l d a g r i d o r / / d o s o m e t h i n g f a i r l y c o m p l e x } } ; r e t u r n { m e t h o d s : _ m e t h o d s } } ) ;
d u c t O p t i o n s C o n t r o l s . j s d e f i n e ( [ ' j q u e r y ' , ' g r i d B u i l d e r ' ] , f u n c t i o n ( $ , g r i d B u i l d e r ) { v a r _ g r i d S i z e = 4 ; v a r _ m e t h o d s = { / * * * R e b u i l d t h e c o l o u r g r i d * / r e b u i l d : f u n c t i o n ( ) { v a r c o l o r s = $ ( t h i s ) . d a t a ( ' c o l o r s ' ) ; v a r g r i d = g r i d B u i l d e r . b u i l d ( _ g r i d S i z e , c o l o r s ) ; $ ( t h i s ) . a p p e n d ( g r i d ) ; } } ; r e t u r n { m e t h o d s : _ m e t h o d s } } ) ;
o d u c t O p t i o n s C o n t r o l s . j s d e f i n e ( [ ' j q u e r y ' , ' m o c k / g r i d B u i l d e r ' , ' p r o d u c t O p t i o n C o n t r o l s ' ] , f u n c t i o n ( $ , g r i d B u i l d e r , p r o d d e s c r i b e ( ' P r o d u c t O p t i o n s C o n t r o l s P l u g i n ' , f u n c t i o n ( ) { v a r $ e l e m e n t ; i t ( ' B u i l d s t h e p r o d u c t o p t i o n s c o n t r o l s ' , f u n c t i o n ( ) { $ e l e m e n t . a t t r ( ' d a t a - c o l o r s ' , ' [ ] ' ) ; g r i d B u i l d e r . b u i l d . a n d . r e t u r n V a l u e ( ' t h e g r i d ' ) ; p r o d u c t O p t i o n C o n t r o l s . m e t h o d s . r e b u i l d . a p p l y ( e l e m e n t ) ; e x p e c t ( g r i d B u i l d e r . b u i l d ) . t o H a v e B e e n C a l l e d W i t h ( 4 , [ ] ) ; e x p e c t ( $ e l e m e n t . h t m l ( ) ) . t o B e ( ' t h e g r i d ' ) ; } ) ; b e f o r e E a c h ( f u n c t i o n ( ) { $ e l e m e n t = $ ( ' < d i v > < / d i v > ' ) . a t t r ( { ' i d ' : ' m a i n ' , ' d a t a - c o l o r s ' : ' ' } ) ; $ ( d o c u m e n t . b o d y ) . a p p e n d ( $ e l e m e n t ) ; } ) ; a f t e r E a c h ( f u n c t i o n ( ) { $ e l e m e n t . r e m o v e ( ) ; } ) ; } ) ; } ) ;
ensure that the mock we use in the spec is the same one that will be loaded by the module we're testing? By using the Require.js mapping configuration:
r u n t . i n i t C o n f i g ( { j a s m i n e : { s r c : [ ] , o p t i o n s : { k e e p R u n n e r : t r u e , s p e c s : ' s p e c / * * / * . j s ' , t e m p l a t e : r e q u i r e ( ' g r u n t - t e m p l a t e - j a s m i n e - r e q u i r e j s ' ) , t e m p l a t e O p t i o n s : { r e q u i r e C o n f i g : { b a s e U r l : ' s r c / ' } } } } } ) ;
r u n t . i n i t C o n f i g ( { j a s m i n e : { s r c : [ ] , o p t i o n s : { k e e p R u n n e r : t r u e , s p e c s : ' s p e c / * * / * . j s ' , t e m p l a t e : r e q u i r e ( ' g r u n t - t e m p l a t e - j a s m i n e - r e q u i r e j s ' ) , t e m p l a t e O p t i o n s : { r e q u i r e C o n f i g : { b a s e U r l : ' s r c / ' , m a p : { ' p r o d u c t O p t i o n C o n t r o l s ' : { ' g r i d B u i l d e r ' : ' m o c k / g r i d B u i l d e r ' } } } } } } } ) ;
r u n t . i n i t C o n f i g ( { j a s m i n e : { s r c : [ ] , o p t i o n s : { k e e p R u n n e r : t r u e , s p e c s : ' s p e c / * * / * . j s ' , t e m p l a t e : r e q u i r e ( ' g r u n t - t e m p l a t e - j a s m i n e - r e q u i r e j s ' ) , t e m p l a t e O p t i o n s : { r e q u i r e C o n f i g : { b a s e U r l : ' s r c / ' , m a p : { ' p r o d u c t O p t i o n C o n t r o l s ' : { ' g r i d B u i l d e r ' : ' m o c k / g r i d B u i l d e r ' } } , c a l l b a c k : f u n c t i o n ( ) { d e f i n e ( ' m o c k / g r i d B u i l d e r ' , f u n c t i o n ( ) { r e t u r n j a s m i n e . c r e a t e S p y O b j ( ' g r i d B u i l d e r ' , [ ' b u i l d ' ] ) ; } ) ; } } } } } } ) ;