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

TDD, Maintainability, and the Happy Front-End D...

TDD, Maintainability, and the Happy Front-End Developer

An amalgamation of previous talks I've given on JS TDD/BDD.

George Pantazis

October 20, 2013
Tweet

More Decks by George Pantazis

Other Decks in Programming

Transcript

  1. NOT: INTEGRATION TESTING [Very broadly] Integration tests combine many modules,

    or view them in the context of pages, and validate that they satisfy the business goals of what we want to deliver.
  2. UNIT TESTING Testing an individual unit of code, such as

    a method (function) in a class. t e s t ( " s u m m e t h o d " , f u n c t i o n ( ) { a s s e r t ( s u m ( 1 , 2 ) = = = 3 ) ; a s s e r t ( s u m ( 1 , n u l l ) = = = n u l l ) ; } ) ;
  3. TEST-DRIVEN DEVELOPMENT [F]irst the developer writes an (initially failing) test

    case that defines a desired improvement or new function, then produces the minimum amount of code to pass that test, and finally refactors the new code to acceptable standards.
  4. BEHAVIOR-DRIVEN DEVELOPMENT Behaviour-driven development is about implementing an application by

    describing its behaviour from the perspective of its stakeholders.
  5. BDD EXAMPLE d e s c r i b e

    ( " C r e a t e s a m e t h o d t h a t a l l o w s t h e u s e r t o a d d t w o n u m b e r s . " , f u n c t i o n ( ) { d e s c r i b e ( " I t a d d s t w o n u m b e r t o g e t h e r . " , f u n c t i o n ( ) { v a r r e s u l t = s u m ( 1 , 2 ) ; e x p e c t ( r e s u l t ) . t o . b e ( 3 ) ; } ) ; d e s c r i b e ( " R e t u r n s a f a l s y r e s u l t i f t h e i n p u t c a n n o t b e s u m m e d . " , f u n c t i o n ( ) { v a r r e s u l t = s u m ( 1 , n u l l ) ; e x p e c t ( r e s u l t ) . t o . b e . f a l s y ; } ) ; } ) ;
  6. A BAD METHOD: / / I n i t i

    a l i z e # f o o . i n i t : f u n c t i o n ( ) { v a r $ f o o = $ ( ' # f o o ' ) ; $ f o o . a d d C l a s s ( ' a c t i v e ' ) ; $ f o o . o n ( ' m o u s e o v e r ' , f u n c t i o n ( ) { $ f o o . f i n d ( ' . d r o p d o w n ' ) . a d d C l a s s ( ' o p e n ' ) ; $ f o o . d a t a ( ' a c t i v e - d r o p d o w n ' , t r u e ) ; } ) ; }
  7. DESCRIPTIVE METHOD NAMES What is your method really doing? i

    n i t B i n d A n d H a n d l e M o u s e O v e r : f u n c t i o n ( ) { . . . } And more often then not... i n i t B i n d A n d H a n d l e M o u s e O v e r A n d L o a d I m a g e s A n d I n c l u d e K
  8. That example is made testable by separating concerns: i n

    i t : f u n c t i o n ( ) { v a r $ e l e m = $ ( ' # f o o ' ) ; $ e l e m . a d d C l a s s ( ' a c t i v e ' ) ; t h i s . b i n d A c t i o n s ( ) ; } , b i n d A c t i o n s : f u n c t i o n ( ) { v a r $ e l e m = $ ( ' # f o o ' ) ; $ e l e m . o n ( ' m o u s e o v e r ' , t h i s . a c t i v a t e D r o p d o w n ) ; } , a c t i v a t e D r o p d o w n : f u n c t i o n ( ) { v a r $ e l e m = $ ( ' # f o o ' ) ; $ e l e m . f i n d ( ' . d r o p d o w n ' ) . a d d C l a s s ( ' o p e n ' ) ; $ e l e m . d a t a ( ' a c t i v e - d r o p d o w n ' , t r u e ) ; }
  9. TESTING I/O The classical test is for input and output

    (I/O). v a r f o o = f u n c t i o n ( I N P U T ) { r e t u r n O U T P U T ; } / / D o I n e e d t o t e s t . . . f o o ( 1 ) ; f o o ( ' f o o ' ) ; f o o ( f a l s e ) ;
  10. DOM CHANGES < ! - - B e f o

    r e - - > < d i v i d = " f o o " > < / d i v > < ! - - A f t e r - - > < d i v i d = " f o o " c l a s s = " a c t i v e " > < / d i v >
  11. COUPLING v a r b i n d A c

    t i o n s = f u n c t i o n ( ) { . . . } / / i n i t * l o o s e l y * c o u p l e d t o b i n d A c t i o n s . v a r i n i t = f u n c t i o n ( ) { / / . . . b i n d A c t i o n s ( ) ; } Was b i n d A c t i o n s ( ) called? How many times? What was it called with?
  12. AN EXAMPLE SUITE d e s c r i b

    e ( " C r e a t e s a n a v t h a t , w h e n h o v e r e d , e x p o s e s a d r o p d o w n . " , f u n c t i o n ( ) { v a r $ o r i g i n a l = $ ( ' # f o o ' ) , $ t e s t e r O b j , i n s t a n c e ; b e f o r e E a c h ( f u n c t i o n ( ) { . . . } ) ; a f t e r E a c h ( f u n c t i o n ( ) { . . . } ) ; d e s c r i b e ( " I n i t i a l i z e s t h e n a v i g a t i o n . " , f u n c t i o n ( ) { . . . } ) ; d e s c r i b e ( " B i n d s h o v e r e v e n t s t o t h e n a v i g a t i o n . " , f u n c t i o n ( ) { . . . } ) ; d e s c r i b e ( " C a n d i s p l a y t h e d r o p d o w n . " , f u n c t i o n ( ) { . . . } ) ; } ) ;
  13. SETUPS AND TEARDOWNS b e f o r e E

    a c h ( f u n c t i o n ( ) { $ t e s t e r O b j = $ o r i g i n a l . c l o n e ( ) ; i n s t a n c e = n e w F o o ( $ t e s t e r O b j ) ; } ) ; a f t e r E a c h ( f u n c t i o n ( ) { $ t e s t e r O b j = i n s t a n c e = n u l l ; } ) ;
  14. FILLING OUT TESTS d e s c r i b

    e ( " I n i t i a l i z e s t h e n a v i g a t i o n . " , f u n c t i o n ( ) { F o o . i n i t ( ) ; v a r c l a s s L i s t = $ t e s t e r O b j [ 0 ] . c l a s s N a m e . s p l i t ( ' ' ) ; e x p e c t ( c l a s s L i s t ) . t o . c o n t a i n ( ' a c t i v e ' ) ; } ) ; d e s c r i b e ( " B i n d s h o v e r e v e n t s t o t h e n a v i g a t i o n . " , f u n c t i o n ( ) { v a r o r i g i n a l C o u n t = $ . _ d a t a ( $ t e s t e r O b j , ' e v e n t s ' ) . l e n g t h ; F o o . b i n d A c t i o n s ( ) ; v a r a f t e r C o u n t = $ . _ d a t a ( $ t e s t e r O b j , ' e v e n t s ' ) . l e n g t h ; e x p e c t ( o r i g i n a l C o u n t + 1 ) . t o . e q u a l ( a f t e r C o u n t ) ; } ) ;
  15. FILLING OUT TESTS (CON'T) d e s c r i

    b e ( " C a n d i s p l a y t h e d r o p d o w n . " , f u n c t i o n ( ) { F o o . a c t i v a t e D r o p d o w n ( ) ; v a r d d C l a s s e s = $ t e s t e r O b j . f i n d ( ' . d r o p d o w n ' ) [ 0 ] . c l a s s N a m e . s p l i t ( ' ' ) ; v a r d a t a = $ t e s t e r O b j . d a t a ( ' a c t i v e - d r o p d o w n ' ) ; e x p e c t ( d d C l a s s e s ) . t o . c o n t a i n ( ' o p e n ' ) ; e x p e c t ( d a t a ) . t o . b e . t r u e ; } ) ;
  16. DON'T WORRY IF YOUR TESTS AREN'T GREAT AT FIRST Test

    writing and test-first is a learned skill.
  17. "IMPERFECT TESTS, RUN FREQUENTLY, ARE MUCH BETTER THAN PERFECT TESTS

    THAT ARE NEVER WRITTEN AT ALL." Martin Fowler
  18. MORE ON WRITING TESTS: Is Unit Testing worth the effort?

    Twelve Benefits of Writing Unit Tests First I Pity the Fool Who Doesn't Write Unit Tests The Deep Synergy between Testability and Good Design
  19. LET'S TALK ABOUT EXCUSES "Testing sounds great, but it's too

    complicated to apply to my project and expect everyone to implement it correctly."
  20. AUTOMATION MAKES IT EASY If setting up a module is

    as simple as yo foo:module, and if that command rigs up your tests for you, the only thing you really need to worry about is writing tests.
  21. LET'S TAKE A LOOK AT G E N E R

    A T O R - H E R O
  22. LET'S TALK SOME MORE ABOUT EXCUSES "We don't have time

    to write tests!" What do you have time for?
  23. CYCLOMATIC COMPLEXITY [T]he number of linearly independent paths through a

    program's source code. For the front-end developer, this is primarily: If/else switch ||
  24. HALSTEAD EFFORT Using the number of operators and operands in

    your code, determines: Difficulty: How hard is your method to understand? Volume: How large is your method? Effort: How long should it have taken to develop that method?
  25. YEP* Instead of three days, perhaps four days of development.

    But what did that buy us? *IN MY OPINION
  26. IN CONCLUSION TDD helps to reduce the things I hate

    the most about development: risk, maintenance, bugs, late nights. TDD helps you focus on the things you love about development: designing and developing new features.