Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Developing AMD modules mockist style

Developing AMD modules mockist style

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.

Avatar for Jon Acker

Jon Acker

June 20, 2014

More Decks by Jon Acker

Other Decks in Programming

Transcript

  1. COMPLEXITY HELL 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 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 ) ; }
  2. WHAT DID IT LOOK LIKE BEFORE AMD THE MODULE PATTERN:

    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 | | { } ) ;
  3. PROBLEMS? Need to create script tags for each file Pollutes

    global namespace Mistakes on file order can break dependencies Conditionally loading scripts is requires messy code
  4. SCRIPT TAGS < s c r i p t s

    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 ? >
  5. WHAT IS AMD ASYNCHRONOUS MODULE DEFINITION A standard for loading

    JS modules asynchronously https://github.com/amdjs/amdjs-api/wiki/AMD Provides a mechanism for managing dependencies Provides an isolated context for each module
  6. BENEFITS OF AMD? ISOLATED EXECUTION CONTEXT / / m y

    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 ) ; } } } ) ;
  7. BENEFITS OF AMD? SPECIFY AND MANAGE DEPENDENCIES / / 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 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 ) ; } } } ) ;
  8. BENEFITS OF AMD? NAMESPACED DEPENDENCIES - NO GLOBALS / /

    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 ) ; } } } ) ;
  9. BENEFITS OF AMD? Neat dependency system Lazy loads module source

    Can load any kind of file Geared towards asynchronous environments (e.g. browser)
  10. AMD LOADERS? IMPLEMENTATIONS OF AMD Require JS Curl JS Small

    - 4k, handles both AMD and CommonJS Almond Minimal implementation of requirejs, suitable for production
  11. GRUNT A javascript build system Has a jasmine plugin that

    provides nice console runner Has a jasmine template plugin that builds HTML spec runner
  12. REQUIREMENTS / / p a c k a g e

    . 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 " } }
  13. DEPENDENCY INJECTION CONTAINER? For mockist TDD, we need some way

    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!
  14. CLASSICAL TDD Mainly concerned with testing a module works as

    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
  15. THE CLASSIC TDD WORKFLOW 1. Write a spec / /

    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 ) ; } ) ;
  16. 3. WRITE AN IMPLEMENTATION / / s r c /

    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 ) ; } } ;
  17. 5. REFACTOR / / s r c / 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 ' , ' 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 } } ) ;
  18. GRID BUILDER MOVES TO ITS OWN AMD / / 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 ; } } } ) ;
  19. IMPLEMENTATION? / / s r 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 ' ] , 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 } } ) ;
  20. IMPLEMENTATION. / / s r 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 ' , ' 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 } } ) ;
  21. SPECIFICATION / / 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 ' , ' 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 ( ) ; } ) ; } ) ; } ) ;
  22. SWAPPING OUT THE REAL MODULES FOR MOCKS How do we

    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:
  23. USING GRUNT TO RUN THE SPECS BASIC GRUNT CONFIGURATION g

    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 / ' } } } } } ) ;
  24. USING GRUNT TO RUN THE SPECS GRUNT MAPPING CONFIGURATION g

    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 ' } } } } } } } ) ;
  25. USING GRUNT TO RUN THE SPECS DEFINE YOUR COLLABORATOR/INTERFACE g

    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 ' ] ) ; } ) ; } } } } } } ) ;