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

Ember test prescription

Ember test prescription

AN INTRODUCTION TO THE BASICS OF UNIT AND ACCEPTANCE TESTING IN EMBER.JS

Andrea Dal Ponte

September 24, 2015
Tweet

Other Decks in Programming

Transcript

  1. EMBER.JS TEST PRESCRIPTION AN INTRODUCTION TO THE BASICS OF UNIT

    AND ACCEPTANCE TESTING IN EMBER.JS Created by / Andrea Dal Ponte @dalpo
  2. TABLE OF CONTENTS: Testing fable Default testing tools Acceptance tests

    Acceptance test helpers Unit testing Unit test helpers Testing components Testing controllers Testing Routes Testing Models
  3. is the default testing framework for Ember.JS, but others are

    supported through third-party addons. QUnit
  4. TESTEM RUN TESTS IN ALL AVAILABLE BROWSERS AS WELL AS

    NODE AND PHANTOMJS $ e m b e r t e s t This command run your tests using Testem to make testing multiple browsers very easy.
  5. ACCEPTANCE TESTS Acceptance tests are used to test user interaction

    and application flow. They emulate user interaction and confirm expected results. Some acceptance tests you might write are: A user is able to log in via the login form. A user is able to create a blog post. A visitor does not have access to the admin panel.
  6. SAMPLE ACCEPTANCE TEST ember-cli comes with acceptance test support out

    of the box. For creating your first test, you just need to run: ember generate acceptance-test <name> $ e m b e r g e n e r a t e a c c e p t a n c e ­ t e s t u s e r ­ c a n ­ l o g i n ­ v i a ­ f o r m v e r s i o n : 1 . 1 3 . 8 i n s t a l l i n g a c c e p t a n c e ­ t e s t c r e a t e t e s t s / a c c e p t a n c e / u s e r ­ c a n ­ l o g i n ­ v i a ­ f o r m ­ t e s t . j s
  7. SHOW ME THE CODE! tests/acceptance/user-can-login-via-form-test.js i m p o r

    t E m b e r f r o m ' e m b e r ' ; i m p o r t { m o d u l e , t e s t } f r o m ' q u n i t ' ; i m p o r t s t a r t A p p f r o m ' t e s t i n g ­ e m b e r / t e s t s / h e l p e r s / s t a r t ­ a p p ' ; m o d u l e ( ' A c c e p t a n c e | u s e r c a n l o g i n v i a f o r m ' , { b e f o r e E a c h : f u n c t i o n ( ) { t h i s . a p p l i c a t i o n = s t a r t A p p ( ) ; } , a f t e r E a c h : f u n c t i o n ( ) { E m b e r . r u n ( t h i s . a p p l i c a t i o n , ' d e s t r o y ' ) ; } } ) ; t e s t ( ' v i s i t i n g / u s e r ­ c a n ­ l o g i n ­ v i a ­ f o r m ' , f u n c t i o n ( a s s e r t ) { v i s i t ( ' / u s e r ­ c a n ­ l o g i n ­ v i a ­ f o r m ' ) ; a n d T h e n ( f u n c t i o n ( ) { a s s e r t . e q u a l ( c u r r e n t U R L ( ) , ' / u s e r ­ c a n ­ l o g i n ­ v i a ­ f o r m ' ) ; } ) ; } ) ;
  8. TESTING EVENT DRIVEN APPS One of the major issues in

    testing web applications is that all code is event-driven.
  9. ACCEPTANCE TEST HELPERS Ember includes several helpers to facilitate acceptance

    testing. There are two types of helpers: asynchronous and synchronous.
  10. ASYNCHRONOUS HELPERS Asynchronous helpers will wait for asynchronous behavior of

    your application, so each helper is only called after the previous one finishes. click(selector) fillIn(selector, text) keyEvent(selector, type, keyCode) triggerEvent(selector, type, options) visit(url)
  11. AND THEN??? The andThen helper will wait for all preceding

    asynchronous helpers to complete prior to progressing forward. / / t e s t s / a c c e p t a n c e / n e w ­ p o s t ­ a p p e a r s ­ f i r s t ­ t e s t . j s t e s t ( ' s i m p l e t e s t ' , f u n c t i o n ( a s s e r t ) { a s s e r t . e x p e c t ( 1 ) ; / / E n s u r e t h a t w e w i l l p e r f o r m o n e a s s e r t i o n v i s i t ( ' / p o s t s / n e w ' ) ; f i l l I n ( ' i n p u t . t i t l e ' , ' M y n e w p o s t ' ) ; c l i c k ( ' b u t t o n . s u b m i t ' ) ; / / W a i t f o r a s y n c h r o n o u s h e l p e r s a b o v e t o c o m p l e t e a n d T h e n ( f u n c t i o n ( ) { / / F i n a l l y m a k e o u r a s s e r t i o n a s s e r t . e q u a l ( f i n d ( ' u l . p o s t s l i : f i r s t ' ) . t e x t ( ) , ' M y n e w p o s t ' ) ; } ) ; } ) ;
  12. CUSTOM HELPER / / t e s t s /

    h e l p e r s / d b l c l i c k . j s i m p o r t E m b e r f r o m ' e m b e r ' ; e x p o r t d e f a u l t E m b e r . T e s t . r e g i s t e r A s y n c H e l p e r ( ' d b l c l i c k ' , f u n c t i o n ( a p p , a s s e r t , s e l e c t o r , c o n t e x t ) { v a r $ e l = f i n d W i t h A s s e r t ( s e l e c t o r , c o n t e x t ) ; E m b e r . r u n ( f u n c t i o n ( ) { $ e l . d b l c l i c k ( ) ; } ) ; } ) ;
  13. UNIT TESTS Unit tests are generally used to test a

    small piece of code and ensure that it is doing what was intended. Testing an E m b e r . O b j e c t is as simple as creating an instance of the object, setting its state, and running assertions against the object.
  14. TESTING OBJECT METHODS / / a p p / m

    o d e l s / s o m e ­ t h i n g . j s e x p o r t d e f a u l t E m b e r . O b j e c t . e x t e n d ( { f o o : ' b a r ' , t e s t M e t h o d ( ) { t h i s . s e t ( ' f o o ' , ' b a z ' ) ; } } ) ; / / t e s t s / u n i t / m o d e l s / s o m e ­ t h i n g ­ t e s t . j s i m p o r t { m o d u l e F o r M o d e l , t e s t } f r o m ' e m b e r ­ q u n i t ' ; m o d u l e F o r M o d e l ( ' s o m e ­ t h i n g ' , ' U n i t | M o d e l | s o m e t h i n g ' , { / / S p e c i f y t h e o t h e r u n i t s t h a t a r e r e q u i r e d f o r t h i s t e s t . n e e d s : [ ] } ) ; t e s t ( ' c a l l i n g t e s t M e t h o d u p d a t e d f o o ' , f u n c t i o n ( a s s e r t ) { v a r s o m e T h i n g = t h i s . s u b j e c t ( { } ) ; s o m e T h i n g . t e s t M e t h o d ( ) ; a s s e r t . e q u a l ( s o m e T h i n g . g e t ( ' f o o ' ) , ' b a z ' ) ; } ) ;
  15. TESTING COMPUTED PROPERTIES / / a p p / m

    o d e l s / s o m e ­ t h i n g . j s e x p o r t d e f a u l t E m b e r . O b j e c t . e x t e n d ( { f o o : ' b a r ' , c o m p u t e d F o o : E m b e r . c o m p u t e d ( ' f o o ' , f u n c t i o n ( ) { r e t u r n ' c o m p u t e d ' + t h i s . g e t ( ' f o o ' ) ; } ) } ) ; / / t e s t s / u n i t / m o d e l s / s o m e ­ t h i n g ­ t e s t . j s i m p o r t { m o d u l e F o r M o d e l , t e s t } f r o m ' e m b e r ­ q u n i t ' ; m o d u l e F o r M o d e l ( ' s o m e ­ t h i n g ' , ' U n i t | M o d e l | s o m e t h i n g ' , { / / S p e c i f y t h e o t h e r u n i t s t h a t a r e r e q u i r e d f o r t h i s t e s t . n e e d s : [ ] } ) ; t e s t ( ' c o m p u t e d F o o c o r r e c t l y c o n c a t s f o o ' , f u n c t i o n ( a s s e r t ) { v a r s o m e T h i n g = t h i s . s u b j e c t ( { } ) ; s o m e T h i n g . s e t ( ' f o o ' , ' b a z ' ) ; a s s e r t . e q u a l ( s o m e T h i n g . g e t ( ' c o m p u t e d F o o ' ) , ' c o m p u t e d b a z ' ) ; } ) ;
  16. TESTING OBSERVERS / / a p p / m o

    d e l s / s o m e ­ t h i n g . j s e x p o r t d e f a u l t E m b e r . O b j e c t . e x t e n d ( { f o o : ' b a r ' , o t h e r : ' n o ' , d o S o m e t h i n g : E m b e r . o b s e r v e r ( ' f o o ' , f u n c t i o n ( ) { t h i s . s e t ( ' o t h e r ' , ' y e s ' ) ; } ) } ) ; / / t e s t s / u n i t / m o d e l s / s o m e ­ t h i n g ­ t e s t . j s i m p o r t { m o d u l e F o r M o d e l , t e s t } f r o m ' e m b e r ­ q u n i t ' ; m o d u l e F o r M o d e l ( ' s o m e ­ t h i n g ' , ' U n i t | M o d e l | s o m e t h i n g ' , { / / S p e c i f y t h e o t h e r u n i t s t h a t a r e r e q u i r e d f o r t h i s t e s t . n e e d s : [ ] } ) ; t e s t ( ' d o S o m e t h i n g o b s e r v e r s e t s o t h e r p r o p ' , f u n c t i o n ( ) { v a r s o m e T h i n g = t h i s . s u b j e c t ( { } ) ; s o m e T h i n g . s e t ( ' f o o ' , ' b a z ' ) ; e q u a l ( s o m e T h i n g . g e t ( ' o t h e r ' ) , ' y e s ' ) ; } ) ;
  17. UNIT TEST HELPERS By including Ember-QUnit, you will have access

    to a number of test helpers: moduleFor(fullName [, description [, callbacks]]) fullName: The full name of the unit, (ie. controller:application, route:index, etc.) description: the description of the module callbacks: normal QUnit callbacks (setup and teardown), with addition to needs. moduleForComponent(name [, description [, callbacks]]) name: the short name of the component that you'd use in a template, (ie. x-foo, ic-tabs, etc.) description: the description of the module callbacks: normal QUnit callbacks (setup and teardown), with addition to needs. moduleForModel(name [, description [, callbacks]]) name: the short name of the model you'd use in store operations (ie. user, assignmentGroup, etc.) description: the description of the module callbacks: normal QUnit callbacks (setup and teardown), with addition to needs. test(description, callbacks) setResolver
  18. UNIT TESTING SETUP In order to unit test your Ember

    application, you need to let Ember know it is in test mode. E m b e r . s e t u p F o r T e s t i n g ( ) This call TURN OFF the Ember run loop execution.
  19. COMPONENT UNIT TESTS An E m b e r .

    C o m p o n e n t extends E m b e r . O b j e c t too i m p o r t { t e s t , m o d u l e F o r C o m p o n e n t } f r o m ' e m b e r ­ q u n i t ' ; m o d u l e F o r C o m p o n e n t ( ' x ­ f o o ' , { u n i t : t r u e , n e e d s : [ ] } ) ; / / r u n a t e s t t e s t ( ' i t r e n d e r s ' , f u n c t i o n ( a s s e r t ) { a s s e r t . e x p e c t ( 1 ) ; / / c r e a t e s t h e c o m p o n e n t i n s t a n c e v a r s u b j e c t = t h i s . s u b j e c t ( ) ; / / r e n d e r t h e c o m p o n e n t o n t h e p a g e t h i s . r e n d e r ( ) ; a s s e r t . e q u a l ( t h i s . $ ( ' . f o o ' ) . t e x t ( ) , ' b a r ' ) ; } ) ;
  20. BUT... Unit tests require you to instantiate your Ember components

    in JavaScript. And you will never use an Ember component in this way on your production code. Components are integrated into your app with templates, and you don't set attributes on them directly either.
  21. COMPONENT INTEGRATION TESTS i m p o r t h

    b s f r o m ' h t m l b a r s ­ i n l i n e ­ p r e c o m p i l e ' ; i m p o r t { t e s t , m o d u l e F o r C o m p o n e n t } f r o m ' e m b e r ­ q u n i t ' ; m o d u l e F o r C o m p o n e n t ( ' x ­ f o o ' , { i n t e g r a t i o n : t r u e } ) ; t e s t ( ' i t r e n d e r s ' , f u n c t i o n ( a s s e r t ) { a s s e r t . e x p e c t ( 2 ) ; / / s e t u p t h e o u t e r c o n t e x t t h i s . s e t ( ' v a l u e ' , ' c a t ' ) ; t h i s . o n ( ' a c t i o n ' , f u n c t i o n ( r e s u l t ) { a s s e r t . e q u a l ( r e s u l t , ' b a r ' ) ; } ) ; / / r e n d e r t h e c o m p o n e n t t h i s . r e n d e r ( h b s ` { { x ­ f o o v a l u e = v a l u e a c t i o n = " r e s u l t " } } ` ) ; a s s e r t . e q u a l ( t h i s . $ ( ' d i v > . v a l u e ' ) . t e x t ( ) , ' c a t ' ) ; t h i s . $ ( ' b u t t o n ' ) . c l i c k ( ) ; } ) ;
  22. TESTING CONTROLLERS Given a sample P o s t s

    C o n t r o l l e r e x p o r t d e f a u l t E m b e r . C o n t r o l l e r . e x t e n d ( { p r o p A : ' Y o u n e e d t o w r i t e t e s t s ' , p r o p B : ' A n d w r i t e o n e f o r m e t o o ' , s e t P r o p B ( s t r ) { t h i s . s e t ( ' p r o p B ' , s t r ) ; } , a c t i o n s : { s e t P r o p s ( s t r ) { t h i s . s e t ( ' p r o p A ' , ' T e s t i n g i s c o o l ' ) ; t h i s . s e t P r o p B ( s t r ) ; } } } ) ;
  23. TESTING CONTROLLER ACTIONS i m p o r t {

    m o d u l e F o r , t e s t } f r o m ' e m b e r ­ q u n i t ' ; m o d u l e F o r ( ' c o n t r o l l e r : p o s t s ­ t e s t ' , { / / S p e c i f y t h e o t h e r u n i t s t h a t a r e r e q u i r e d f o r t h i s t e s t . / / n e e d s : [ ' c o n t r o l l e r : f o o ' ] } ) ; t e s t ( ' c a l l i n g t h e a c t i o n s e t P r o p s u p d a t e s p r o p s ' , f u n c t i o n ( a s s e r t ) { a s s e r t . e x p e c t ( 2 ) ; / / g e t t h e c o n t r o l l e r i n s t a n c e v a r c t r l = t h i s . s u b j e c t ( ) ; / / t r i g g e r t h e a c t i o n o n t h e c o n t r o l l e r b y u s i n g t h e ` s e n d ` m e t h o d , / / p a s s i n g i n a n y p a r a m s t h a t o u r a c t i o n m a y b e e x p e c t i n g c t r l . s e n d ( ' s e t P r o p s ' , ' T e s t i n g R o c k s ! ' ) ; / / f i n a l l y w e a s s e r t t h a t o u r v a l u e s h a v e b e e n u p d a t e d / / b y t r i g g e r i n g o u r a c t i o n . a s s e r t . e q u a l ( c t r l . g e t ( ' p r o p A ' ) , ' T e s t i n g i s c o o l ' ) ; a s s e r t . e q u a l ( c t r l . g e t ( ' p r o p B ' ) , ' T e s t i n g R o c k s ! ' ) ; } ) ;
  24. TESTING ROUTES Given a sample A p p l i

    c a t i o n R o u t e with an alert function d i s p l a y A l e r t : e x p o r t d e f a u l t E m b e r . R o u t e . e x t e n d ( { a c t i o n s : { d i s p l a y A l e r t ( t e x t ) { t h i s . _ d i s p l a y A l e r t ( t e x t ) ; } } , _ d i s p l a y A l e r t ( t e x t ) { a l e r t ( t e x t ) ; } } ) ;
  25. TESTING ROUTES We are going to stub the w i

    n d o w . a l e r t function / / t e s t s / u n i t / r o u t e s / a p p l i c a t i o n ­ t e s t . j s v a r o r i g i n a l A l e r t ; m o d u l e F o r ( ' r o u t e : a p p l i c a t i o n ' , { b e f o r e E a c h ( ) { o r i g i n a l A l e r t = w i n d o w . a l e r t ; / / s t o r e a r e f e r e n c e t o w i n d o w . a l e r t } , a f t e r E a c h ( ) { w i n d o w . a l e r t = o r i g i n a l A l e r t ; / / r e s t o r e o r i g i n a l w i n d o w . a l e r t } } ) ;
  26. TESTING ROUTES / / t e s t s /

    u n i t / r o u t e s / a p p l i c a t i o n ­ t e s t . j s t e s t ( ' A l e r t i s c a l l e d o n d i s p l a y A l e r t ' , f u n c t i o n ( a s s e r t ) { a s s e r t . e x p e c t ( 2 ) ; v a r r o u t e = t h i s . s u b j e c t ( ) ; / / g e t t h e r o u t e i n s t a n c e v a r e x p e c t e d T e x t ; e x p e c t e d T e x t = ' f o o ' ; w i n d o w . a l e r t = f u n c t i o n ( t e x t ) { a s s e r t . e q u a l ( t e x t , e x p e c t e d T e x t ) } ; r o u t e . _ d i s p l a y A l e r t ( e x p e c t e d T e x t ) ; e x p e c t e d T e x t = ' b a r ' ; w i n d o w . a l e r t = f u n c t i o n ( t e x t ) { a s s e r t . e q u a l ( t e x t , e x p e c t e d T e x t ) } ; r o u t e . s e n d ( ' d i s p l a y A l e r t ' , e x p e c t e d T e x t ) ; } ) ;
  27. TESTING MODELS Given a sample Ember Data model: / /

    a p p / m o d e l s / p l a y e r . j s e x p o r t d e f a u l t D S . M o d e l . e x t e n d ( { l e v e l : D S . a t t r ( ' n u m b e r ' , { d e f a u l t V a l u e : 0 } ) , l e v e l N a m e : D S . a t t r ( ' s t r i n g ' , { d e f a u l t V a l u e : ' N o o b ' } ) , l e v e l U p ( ) { v a r n e w L e v e l = t h i s . i n c r e m e n t P r o p e r t y ( ' l e v e l ' ) ; i f ( n e w L e v e l = = = 5 ) { t h i s . s e t ( ' l e v e l N a m e ' , ' P r o f e s s i o n a l ' ) ; } } } ) ;
  28. TESTING MODELS Ember Data Models can be tested using the

    m o d u l e F o r M o d e l helper: m o d u l e F o r M o d e l ( ' p l a y e r ' ) ; t e s t ( ' l e v e l U p ' , f u n c t i o n ( a s s e r t ) { / / t h i s . s u b j e c t a l i a s e s t h e c r e a t e R e c o r d m e t h o d o n t h e m o d e l v a r p l a y e r = t h i s . s u b j e c t ( { l e v e l : 4 } ) ; / / w r a p a s y n c h r o n o u s c a l l i n r u n l o o p E m b e r . r u n ( f u n c t i o n ( ) { p l a y e r . l e v e l U p ( ) ; } ) ; a s s e r t . e q u a l ( p l a y e r . g e t ( ' l e v e l ' ) , 5 ) ; a s s e r t . e q u a l ( p l a y e r . g e t ( ' l e v e l N a m e ' ) , ' P r o f e s s i o n a l ' ) ; } ) ;
  29. TESTING RELATIONSHIP Assume that a U s e r can

    own a P r o f i l e . / / a p p / m o d e l s / p r o f i l e . j s e x p o r t d e f a u l t D S . M o d e l . e x t e n d ( { / / c o d e . . . } ) ; / / a p p / m o d e l s / u s e r . j s e x p o r t d e f a u l t D S . M o d e l . e x t e n d ( { p r o f i l e : D S . b e l o n g s T o ( ' p r o f i l e ' ) / / c o d e . . . } ) ;
  30. TESTING RELATIONSHIP Then you could test that the relationship is

    wired up correctly. / / t e s t s / u n i t / m o d e l s / u s e r ­ t e s t . j s m o d u l e F o r M o d e l ( ' u s e r ' , { / / S p e c i f y t h e o t h e r u n i t s t h a t a r e r e q u i r e d f o r t h i s t e s t . n e e d s : [ ' m o d e l : p r o f i l e ' ] } ) ; t e s t ( ' p r o f i l e r e l a t i o n s h i p ' , f u n c t i o n ( a s s e r t ) { v a r U s e r = t h i s . s t o r e ( ) . m o d e l F o r ( ' u s e r ' ) ; v a r p r o f i l e = E m b e r . g e t ( U s e r , ' r e l a t i o n s h i p s B y N a m e ' ) . g e t ( ' p r o f i l e ' ) ; a s s e r t . e q u a l ( p r o f i l e . k e y , ' p r o f i l e ' ) ; a s s e r t . e q u a l ( p r o f i l e . k i n d , ' b e l o n g s T o ' ) ; } ) ;
  31. THE END REFERENCES: Ember test guide: Ember conf: Slides at:

    About me: guides.emberjs.com/v2.0.0/testing/ The unofficial official ember testing guide github.com/dalpo/ember-test-prescription andreadalponte.com