within this presentation, we are primarely talking about unit testing, ie tests written by developers while writing code. Writing unit tests should be a part of the workflow of all developers in the team.
a r s e _ i n t e g e r ( s t r i n g _ i n ) : " " " R e t u r n a n i n t e g e r f r o m a s t r i n g o r e l s e N o n e . " " " t r y : r e t u r n i n t ( s t r i n g _ i n ) e x c e p t V a l u e E r r o r : r e t u r n N o n e d e f w i l l _ t h e _ s n o w _ m e l t _ t o d a y ( t e m p e r a t u r e _ s t r i n g ) : " " " T e l l t h e u s e r w h e t h e r t h e s n o w w i l l m e l t t o d a y . " " " t e m p e r a t u r e _ i n t e g e r = p a r s e _ i n t e g e r ( t e m p e r a t u r e _ s t r i n g ) i f t e m p e r a t u r e _ i n t e g e r i s N o n e : r e t u r n " I d o n ' t k n o w w h a t t h a t m e a n s ! " i f t e m p e r a t u r e _ i n t e g e r > 3 : r e t u r n " I t p r o b a b l y w i l l . " r e t u r n " N o p e ! I t ' s h e r e t o s t a y . "
s e _ i n t e g e r ( s t r i n g _ i n ) : " " " R e t u r n a n i n t e g e r f r o m a s t r i n g o r e l s e N o n e . " " " t r y : r e t u r n i n t ( s t r i n g _ i n ) e x c e p t V a l u e E r r o r : r e t u r n F a l s e # W e d e c i d e t h a t w e ' d p r e f e r t o r e t u r n F a l s e h e r e . d e f w i l l _ t h e _ s n o w _ m e l t _ t o d a y ( t e m p e r a t u r e _ s t r i n g ) : " " " T e l l t h e u s e r w h e t h e r t h e s n o w w i l l m e l t t o d a y . " " " t e m p e r a t u r e _ i n t e g e r = p a r s e _ i n t e g e r ( t e m p e r a t u r e _ s t r i n g ) i f n o t t e m p e r a t u r e _ i n t e g e r : # W e " f i x " t h i s l i n e t o c h e c k f o r F a l s e . r e t u r n " I d o n ' t k n o w w h a t t h a t m e a n s ! " i f t e m p e r a t u r e _ i n t e g e r > 3 : r e t u r n " I t p r o b a b l y w i l l . " r e t u r n " N o p e ! I t ' s h e r e t o s t a y . "
_ t h e _ s n o w _ m e l t _ t o d a y ( ' - 5 ' ) > > > " N o p e ! I t ' s h e r e t o s t a y . " > > > w i l l _ t h e _ s n o w _ m e l t _ t o d a y ( ' 2 0 ' ) > > > " I t p r o b a b l y w i l l . " > > > w i l l _ t h e _ s n o w _ m e l t _ t o d a y ( ' 0 ' ) > > > " I d o n ' t k n o w w h a t t h a t m e a n s ! " (Imagine if we did everything manually...)
u n i t t e s t f r o m w i l l _ t h e _ s n o w _ m e l t i m p o r t w i l l _ i t _ s n o w _ t o d a y c l a s s T e s t W i l l T h e S n o w M e l t ( u n i t t e s t . T e s t C a s e ) : d e f t e s t _ s n o w _ m e l t s _ a b o v e _ t h r e e _ d e g r e e s ( s e l f ) : a n s w e r = w i l l _ i t _ s n o w _ t o d a y ( ' 5 ' ) s e l f . a s s e r t E q u a l ( " I t p r o b a b l y w i l l . " , a n s w e r ) d e f t e s t _ s n o w _ d o e s _ n o t _ m e l t _ b e l o w _ t h r e e _ d e g r e e s ( s e l f ) : a n s w e r = w i l l _ i t _ s n o w _ t o d a y ( ' 0 ' ) s e l f . a s s e r t E q u a l ( " N o p e ! I t ' s h e r e t o s t a y . " , a n s w e r ) d e f t e s t _ n o n _ n u m b e r _ c o n f u s e s _ t h e _ f u n c t i o n ( s e l f ) : a n s w e r = w i l l _ i t _ s n o w _ t o d a y ( ' l u m i ' ) s e l f . a s s e r t E q u a l ( " I d o n ' t k n o w w h a t t h a t m e a n s ! " , a n s w e r )
k ( m o s t r e c e n t c a l l l a s t ) : F i l e " f o o . p y " , l i n e 4 4 , i n t e s t _ s n o w _ d o e s _ n o t _ m e l t _ b e l o w _ t h r e e _ d e g r e e s s e l f . a s s e r t E q u a l ( " N o p e ! I t ' s h e r e t o s t a y . " , a n s w e r ) A s s e r t i o n E r r o r : " N o p e ! I t ' s h e r e t o s t a y . " ! = " I d o n ' t k n o w w h a t t h a t m e a n s ! " - N o p e ! I t ' s h e r e t o s t a y . + I d o n ' t k n o w w h a t t h a t m e a n s !
Remember, unit tests should be as independant as possible. Try to keep applications reusable by not depending tests on other project applications. Once you have more than a few tests, consider creating a t e s t s folder and clearly separating different types of tests into separate files. For example: b l o g / b l o g / t e s t s / b l o g / t e s t s / _ _ i n i t _ _ . p y b l o g / t e s t s / t e s t _ m o d e l s . p y b l o g / t e s t s / t e s t _ v i e w s . p y b l o g / t e s t s / t e s t _ t e m p l a t e s . p y
o n m a n a g e . p y t e s t Running a single app p y t h o n m a n a g e . p y t e s t b l o g Running a single test case p y t h o n m a n a g e . p y t e s t b l o g . t e s t s . B l o g P o s t V i e w T e s t C a s e Running a single test in a test case p y t h o n m a n a g e . p y t e s t b l o g . t e s t s . B l o g P o s t V i e w T e s t C a s e . t e s t _ v i e w _ r e n d e r s Running all tests under a path p y t h o n m a n a g e . p y t e s t b l o g /
automatically create one when running tests. The normal database set up for the application will never be touched! After tests finish, Django will delete the created test database automatically.
we're likely a lot of the time going to need to make requests to the app. Easy! f r o m d j a n g o . t e s t i m p o r t C l i e n t c l i e n t = C l i e n t ( ) r e s p o n s e = c l i e n t . g e t ( ' / ' ) r e s p o n s e = c l i e n t . p o s t ( ' / p o s t s / e d i t / 1 / ' , { " t e x t " : " T e x t c o n t e n t " } ) The response from client provides everything you need to check what happened, including status code, rendered HTML, context, request, templates used, etc. Note! Client is not browser emulation. You cannot do UI tests with it.
d l i b r a r y u n i t t e s t d j a n g o . t e s t L i v e S e r v e r T e s t C a s e T e s t C a s e T r a n s a c t i o n T e s t C a s e S i m p l e T e s t C a s e T e s t C a s e source: Django testing documentation These are ripe for subclassing and extending to your own project's needs.
All T e s t C a s e subclasses 2. Any other Django based tests 3. Any other pure u n i t t e s t . T e s t C a s e tests This ensures all (transactional) T e s t C a s e tests always run with a clean database.
n g o . c o r e . u r l r e s o l v e r s i m p o r t r e v e r s e f r o m d j a n g o . t e s t i m p o r t T e s t C a s e c l a s s H o m e p a g e T e s t C a s e ( T e s t C a s e ) : d e f t e s t _ u s e r _ c a n _ v i e w _ h o m e p a g e ( s e l f ) : r e s p o n s e = s e l f . c l i e n t . g e t ( r e v e r s e ( ' p o s t _ l i s t ' ) ) s e l f . a s s e r t C o n t a i n s ( r e s p o n s e , ' D j a n g o G i r l s B l o g ' )
_ s u p e r u s e r _ c a n _ a d d _ n e w _ b l o g _ p o s t ( s e l f ) : t i t l e = ' A w e s o m e T i t l e 2 ' t e x t = ' H e r e i s l o t s o f p o s t c o n t e n t . . . b l a h b l a h b l a h . . . ' r e s p o n s e = s e l f . c l i e n t . p o s t ( r e v e r s e ( ' p o s t _ n e w ' ) , { ' t i t l e ' : t i t l e , ' t e x t ' : t e x t , } , f o l l o w = T r u e ) p o s t = P o s t . o b j e c t s . g e t ( t i t l e = t i t l e ) s e l f . a s s e r t R e d i r e c t s ( r e s p o n s e , r e v e r s e ( ' p o s t _ d e t a i l ' , k w a r g s = { ' p k ' : p o s t . p k } ) ) s e l f . a s s e r t C o n t a i n s ( r e s p o n s e , t i t l e )
s e t U p ( s e l f ) : " " " A d d s o m e o b j e c t s t o t h e d a t a b a s e t h a t a l l t e s t s w i l l n e e d . " " " s e l f . u s e r = U s e r . o b j e c t s . c r e a t e _ s u p e r u s e r ( u s e r n a m e = ' a d m i n ' , e m a i l = ' ' , p a s s w o r d = ' a d m i n ' ) s e l f . p o s t = s e l f . _ c r e a t e _ p o s t ( a u t h o r = U s e r . o b j e c t s . g e t ( u s e r n a m e = ' a d m i n ' ) , t i t l e = ' T e s t T i t l e 1 ' , t e x t = ' H e r e i s l o t s o f p o s t c o n t e n t . . . b l a h b l a h b l a h . . . ' ) s e l f . c l i e n t . l o g i n ( u s e r n a m e = ' a d m i n ' , p a s s w o r d = ' a d m i n ' )
o m d j a n g o . t e s t i m p o r t L i v e S e r v e r T e s t C a s e f r o m s e l e n i u m . w e b d r i v e r . f i r e f o x . w e b d r i v e r i m p o r t W e b D r i v e r c l a s s S e l e n i u m T e s t C a s e ( L i v e S e r v e r T e s t C a s e ) : d e f t e s t _ b r o w s e _ t o _ p a g e ( s e l f ) : # B r o w s e t o t h e l o g i n p a g e s e l f . s e l e n i u m . g e t ( s e l f . b u i l d _ a b s o l u t e _ u r l ( ' / a d m i n / l o g i n / ? n e x t = / ' ) ) # F i l l o u t i n p u t u s e r n a m e _ i n p u t = s e l f . s e l e n i u m . f i n d _ e l e m e n t _ b y _ n a m e ( " u s e r n a m e " ) u s e r n a m e _ i n p u t . s e n d _ k e y s ( ' a d m i n ' ) p a s s w o r d _ i n p u t = s e l f . s e l e n i u m . f i n d _ e l e m e n t _ b y _ n a m e ( " p a s s w o r d " ) p a s s w o r d _ i n p u t . s e n d _ k e y s ( ' s e c r e t p a s s w o r d ' ) # L o g i n s e l f . s e l e n i u m . f i n d _ e l e m e n t _ b y _ x p a t h ( ' / / i n p u t [ @ v a l u e = " L o g i n " ] ' ) . c l i c k ( )
missing blog publishing functionality, which is bad, so we will add it now. We could just write the code for it, but instead we will write a test for it. c l a s s S u p e r U s e r T e s t C a s e ( T e s t C a s e ) : d e f t e s t _ s u p e r u s e r _ c a n _ p u b l i s h _ b l o g _ p o s t ( s e l f ) : r e s p o n s e = s e l f . c l i e n t . p o s t ( r e v e r s e ( " p o s t _ p u b l i s h " , k w a r g s = { " p k " : s e l f . p o s t . p k } ) ) s e l f . a s s e r t R e d i r e c t s ( r e s p o n s e , r e v e r s e ( " p o s t _ d e t a i l " , k w a r g s = { " p k " : s e l f . p o s t . p k } ) ) p o s t = P o s t . o b j e c t s . g e t ( i d = s e l f . p o s t . p k ) s e l f . a s s e r t I s N o t N o n e ( p o s t . p u b l i s h e d _ d a t e ) Note 1: if this was a live server test case, we could browse to the post and click the button. While that is fine, the publish view can be separately and individually tested by directly posting to it. Note 2: Of course our class should have a s e t U p which creates a user, an (unpublished) blog post, and logs the user in.
since the functionality does not exist. $ p y t h o n m a n a g e . p y t e s t b l o g . t e s t s . S u p e r U s e r T e s t C a s e C r e a t i n g t e s t d a t a b a s e f o r a l i a s ' d e f a u l t ' . . . . . E . . = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = E R R O R : t e s t _ s u p e r u s e r _ c a n _ p u b l i s h _ b l o g _ p o s t ( b l o g . t e s t s . S u p e r U s e r T e s t C a s e ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - T r a c e b a c k ( m o s t r e c e n t c a l l l a s t ) : F i l e " / h o m e / j a s o n / w o r k s p a c e / d j a n g o g i r l s / b l o g / t e s t s . p y " , l i n e 4 9 , i n t e s t _ s u p e r u s e r _ c a n _ r e s p o n s e = s e l f . c l i e n t . p o s t ( r e v e r s e ( " p o s t _ p u b l i s h " , k w a r g s = { " p k " : s e l f . p o s t . p k } ) ) F i l e " / h o m e / j a s o n / . v i r t u a l e n v s / d j a n g o g i r l s / l i b / p y t h o n 3 . 4 / s i t e - p a c k a g e s / d j a n g o / c o r e / u r l r r e t u r n i r i _ t o _ u r i ( r e s o l v e r . _ r e v e r s e _ w i t h _ p r e f i x ( v i e w , p r e f i x , * a r g s , * * k w a r g s ) ) F i l e " / h o m e / j a s o n / . v i r t u a l e n v s / d j a n g o g i r l s / l i b / p y t h o n 3 . 4 / s i t e - p a c k a g e s / d j a n g o / c o r e / u r l r ( l o o k u p _ v i e w _ s , a r g s , k w a r g s , l e n ( p a t t e r n s ) , p a t t e r n s ) ) d j a n g o . c o r e . u r l r e s o l v e r s . N o R e v e r s e M a t c h : R e v e r s e f o r ' p o s t _ p u b l i s h ' w i t h a r g u m e n t s - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - R a n 5 t e s t s i n 0 . 4 1 6 s
add a link in the template for this feature, we should write a test for it too. We know our requirements says the button/link should have the text "Publish" - so we can write a test for it even though we don't know yet how we will implement it - a button, A- tag, etc. d e f t e s t _ s u p e r u s e r _ h a s _ p u b l i s h _ b u t t o n _ i n _ u n p u b l i s h e d _ b l o g _ p o s t ( s e l f ) : r e s p o n s e = s e l f . c l i e n t . g e t ( r e v e r s e ( " p o s t _ d e t a i l " , k w a r g s = { " p k " : s e l f . p o s t . p k } ) ) s e l f . a s s e r t C o n t a i n s ( r e s p o n s e , " P u b l i s h " )
fail. F A I L : t e s t _ s u p e r u s e r _ h a s _ p u b l i s h _ b u t t o n _ i n _ u n p u b l i s h e d _ b l o g _ p o s t ( b l o g . t e s t s . S u p e r U s e r T e s - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - T r a c e b a c k ( m o s t r e c e n t c a l l l a s t ) : F i l e " / h o m e / j a s o n / w o r k s p a c e / d j a n g o g i r l s / b l o g / t e s t s . p y " , l i n e 6 1 , i n t e s t _ s u p e r u s e r _ h a s _ s e l f . a s s e r t C o n t a i n s ( r e s p o n s e , " P u b l i s h " ) F i l e " / h o m e / j a s o n / . v i r t u a l e n v s / d j a n g o g i r l s / l i b / p y t h o n 3 . 4 / s i t e - p a c k a g e s / d j a n g o / t e s t / t e s t m s g _ p r e f i x + " C o u l d n ' t f i n d % s i n r e s p o n s e " % t e x t _ r e p r ) A s s e r t i o n E r r o r : F a l s e i s n o t t r u e : C o u l d n ' t f i n d ' P u b l i s h ' i n r e s p o n s e
which here means: Add P o s t . p u b l i s h ( ) model method (if not already) Add view with name p o s t _ p u b l i s h that publishes post Add to the template a publish button or link
h o n m a n a g e . p y t e s t b l o g C r e a t i n g t e s t d a t a b a s e f o r a l i a s ' d e f a u l t ' . . . . . . . . . - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - R a n 6 t e s t s i n 0 . 5 1 0 s O K D e s t r o y i n g t e s t d a t a b a s e f o r a l i a s ' d e f a u l t ' . . . No more failures!
code and add a test later. It is however also a bad thing to do. Once the code is finished, you will be tempted, or forced by deadlines, to just commit the code and move on. Result: no test coverage You will be writing the test to pass which is always worse than writing a test to fail. When writing a test to pass, it is possible the test doesn't even fail if the feature breaks. When writing a test to fail, you will see it start passing once the feature is implemented. This means it will also break if the feature stops working.
design. Creating the test is like writing out the specifications. You will more likely spot problems in for example business logic before wasting time writing code.
f t e s t _ v i e w _ p u b l i s h i n g _ s t u f f ( s e l f ) : Good: d e f t e s t _ s t a f f _ m e m b e r _ c a n _ p u b l i s h _ b l o g _ p o s t ( s e l f ) : Keep tests small - above example should only test if staff member can publish a post, as the name says.
tests getting you down? Don't worry. Instead, when you fix or change something, or write a new feature - write a test for it. Your code coverage will increasy organically. Don't try to reserve time to implement code coverage - that time will never become available!