Slide 1

Slide 1 text

Automated tests Facts and myths / Wojciech Sznapka @sznapka

Slide 2

Slide 2 text

Cześć

Slide 3

Slide 3 text

An introduction I work in software industry for about 9 years Care a lot about robust and testable architectures Loves software craftsmanship, sophisticated architectures, Big Data and ice hockey

Slide 4

Slide 4 text

Test Driven Development What is this?

Slide 5

Slide 5 text

Add a test

Slide 6

Slide 6 text

Write an implementation

Slide 7

Slide 7 text

Refactor code

Slide 8

Slide 8 text

Repeat!

Slide 9

Slide 9 text

What's important here?

Slide 10

Slide 10 text

... to have tests

Slide 11

Slide 11 text

You won’t go to hell if you’ll write a test after declaring an interface or prototyping a class ...

Slide 12

Slide 12 text

... but you’ll surely end up in hell, if there won’t be a test coverage

Slide 13

Slide 13 text

Myths about automated tests

Slide 14

Slide 14 text

Boss doesn't pay for automated tests

Slide 15

Slide 15 text

It's a myth! A 2005 study found that using TDD meant writing more tests and, in turn, programmers who wrote more tests tended to be more productive by American Scientists

Slide 16

Slide 16 text

But this ain't that hard ... c l a s s R o m a n C o n v e r t e r { p r o t e c t e d $ c o n v e r s i o n s = [ 1 0 0 0 = > ' M ' , 9 0 0 = > ' C M ' , 5 0 0 = > ' D ' , 4 0 0 = > ' C D ' , 1 0 0 = > ' C ' , 9 0 = > ' X C ' , 5 0 = > ' L ' , 4 0 = > ' X L ' , 1 0 = > ' X ' , 9 = > ' I X ' , 5 = > ' V ' , 4 = > ' I V ' , 1 = > ' I ' ] ; p u b l i c f u n c t i o n c o n v e r t ( $ i n A r a b i c ) { i f ( ! i s _ n u m e r i c ( $ i n A r a b i c ) ) { t h r o w n e w \ I n v a l i d A r g u m e n t E x c e p t i o n ( ' I c o n v e r t n u m e r i c s ' ) ; } i f ( $ i n A r a b i c < = 0 ) { r e t u r n ' ' ; } l i s t ( $ a r a b i c , $ r o m a n ) = $ t h i s - > c o n v e r s i o n F a c t o r F o r ( $ i n A r a b i c ) ; r e t u r n $ r o m a n . $ t h i s - > c o n v e r t ( $ i n A r a b i c - $ a r a b i c ) ; } p r o t e c t e d f u n c t i o n c o n v e r s i o n F a c t o r F o r ( $ i n A r a b i c ) { f o r e a c h ( $ t h i s - > c o n v e r s i o n s a s $ a r a b i c = > $ r o m a n ) { i f ( $ a r a b i c < = $ i n A r a b i c ) { r e t u r n [ $ a r a b i c , $ r o m a n ] ; } } } }

Slide 17

Slide 17 text

... start from obvious things c l a s s R o m a n C o n v e r t e r T e s t e x t e n d s P H P U n i t _ F r a m e w o r k _ T e s t C a s e { p u b l i c f u n c t i o n t e s t E m p t y ( ) { $ t h i s - > a s s e r t E q u a l s ( ' ' , ( n e w R o m a n C o n v e r t e r ) - > c o n v e r t ( ' ' ) ) ; } }

Slide 18

Slide 18 text

... provide some meat ... c l a s s R o m a n C o n v e r t e r T e s t e x t e n d s P H P U n i t _ F r a m e w o r k _ T e s t C a s e { / * * @ d a t a P r o v i d e r p r o v i d e T e s t D a t a * / p u b l i c f u n c t i o n t e s t C o n v e r s i o n s ( $ a r a b i c , $ r o m a n ) { $ c o n v e r t e r = n e w R o m a n C o n v e r t e r ( ) ; $ t h i s - > a s s e r t E q u a l s ( $ r o m a n , $ c o n v e r t e r - > c o n v e r t ( $ a r a b i c ) ) ; } p u b l i c f u n c t i o n p r o v i d e T e s t D a t a ( ) { r e t u r n a r r a y [ [ 3 4 9 7 , ' M M M C D X C V I I ' ] , [ 1 , ' I ' ] , [ 2 , ' I I ' ] , [ 6 , ' V I ' ] , [ 9 , ' I X ' ] , [ 4 0 , ' X L ' ] , [ 4 5 , ' X L V ' ] , [ 9 0 , ' X C ' ] , [ 1 0 0 , ' C ' ] , [ 4 0 0 , ' C D ' ] ] ; } }

Slide 19

Slide 19 text

... test edge cases c l a s s R o m a n C o n v e r t e r T e s t e x t e n d s P H P U n i t _ F r a m e w o r k _ T e s t C a s e { / * * @ e x p e c t e d E x c e p t i o n \ I n v a l i d A r g u m e n t E x c e p t i o n * / p u b l i c f u n c t i o n t e s t E m p t y ( ) { ( n e w R o m a n C o n v e r t e r ) - > c o n v e r t ( ' w t f I a m p a s s i n g h e r e ' ) ) ; } }

Slide 20

Slide 20 text

Help yourself with tests generation p h p u n i t - s k e l g e n - - t e s t R o m a n C o n v e r t e r

Slide 21

Slide 21 text

... or using Symfony's XSolveUnitSkelgenBundle . / a p p / c o n s o l e x s o l v e : s k e l g e n : t e s t A c m e / E x a m p l e B u n d l e / S e r v i c e / . . / a p p / c o n s o l e x s o l v e : s k e l g e n : t e s t A c m e / * / C o n t r o l l e r / *

Slide 22

Slide 22 text

We are building too complicated system for an automated tests

Slide 23

Slide 23 text

TDD forces you to write cleaner code

Slide 24

Slide 24 text

It's easier to test smaller units of code

Slide 25

Slide 25 text

so you write smaller classes which are less coupled and that makes your system more stable and open for an extension in the future

Slide 26

Slide 26 text

Your Object Oriented design becomes S.O.L.I.D. compilant and this is awesome!

Slide 27

Slide 27 text

You will of course tackle costly dependencies

Slide 28

Slide 28 text

Use PHPUnit's mock framework or Mockery

Slide 29

Slide 29 text

Fake it till you make it c l a s s M o c k e d T e s t e x t e n d s P H P U n i t _ F r a m e w o r k _ T e s t C a s e { p u b l i c f u n c t i o n t e s t N o n E x i s t i n g V a l u e O b j e c t s ( ) { $ c o n f i g u r a t i o n = \ M o c k e r y : : m o c k ( ' \ C o n f i g u r a t i o n V a l u e O b j e c t ' , [ ' g e t U r l ' = > ' h t t p : / / t d d . s z n a p k a . p l ' , ' g e t F o r m a t ' = > ' x m l ' ] ) ; $ t h i s - > a s s e r t E q u a l s ( ' x m l ' , $ c o n f i g u r a t i o n - > g e t F o r m a t ( ) ) ; / / O K } }

Slide 30

Slide 30 text

Mock things that can't be tested quickly or non- reproducable c l a s s M o c k e d T e s t e x t e n d s P H P U n i t _ F r a m e w o r k _ T e s t C a s e { p u b l i c f u n c t i o n t e s t A p i C a l l s ( ) { $ b u z z M o c k = \ M o c k e r y : : m o c k ( ' \ B u z z \ B r o w s e r ' ) ; $ b u z z M o c k - > s h o u l d R e c e i v e ( ' g e t ' ) - > a n d R e t u r n ( ' < r e s p o n s e > < m o o d > A w e s o m i t y < / m o o d > < / r e s p o n s e > ' ) ; / / t h i s a l s o i s w i s e s o l u t i o n , t o w r i t e x m l f i x t u r e s i n f i l e / / $ b u z z M o c k - > s h o u l d R e c e i v e ( ' g e t ' ) - > o n c e ( ) / / - > a n d R e t u r n ( f i l e _ g e t _ c o n t e n t s ( _ _ D I R _ _ . ' / f i x t u r e s . x m l ' ) ) ; $ a p i = n e w \ A p i C o n s u m e r ( $ b u z z M o c k ) ; $ a p i - > a s s e r t E q u a l s ( ' A w e s o m i t y ' , $ a p i - > g e t C u r r e n t M o o d ( ) ) ; / / O K } }

Slide 31

Slide 31 text

Expect declared behaviors c l a s s M o c k e d T e s t e x t e n d s P H P U n i t _ F r a m e w o r k _ T e s t C a s e { p u b l i c f u n c t i o n t e s t E x p e c t a t i o n s D e c l a r a t i o n s ( ) { $ b u z z M o c k = \ M o c k e r y : : m o c k ( ' \ B u z z \ B r o w s e r ' ) ; $ b u z z M o c k - > s h o u l d R e c e i v e ( ' g e t ' ) - > a n d R e t u r n ( ' < r e s p o n s e > < m o o d > A w e s o m i t y < / m o o d > < / r e s p o n s e > ' ) ; $ l o g g e r M o c k = \ M o c k e r y : : m o c k ( ' \ M o n o l o g \ L o g g e r ' ) ; / / w e j u s t w a n t t o b e s u r e t h a t L o g g e r : : i n f o w a s c a l l e d o n l y o n c e $ l o g g e r M o c k - > s h o u l d R e c e i v e ( ' i n f o ' ) - > o n c e ( ) ; $ a p i = n e w \ A p i C o n s u m e r ( $ b u z z M o c k , $ l o g g e r M o c k ) ; $ a p i - > a s s e r t E q u a l s ( ' A w e s o m i t y ' , $ a p i - > g e t C u r r e n t M o o d ( ) ) ; / / O K } }

Slide 32

Slide 32 text

Be prepared for failures and check if you prepared for unexpected situations c l a s s M o c k e d T e s t e x t e n d s P H P U n i t _ F r a m e w o r k _ T e s t C a s e { / * * @ e x p e c t e d E x c e p t i o n \ M y E x c e p t i o n W r a p p e r * / p u b l i c f u n c t i o n t e s t F a i l e d C o n n e c t i o n ( ) { $ b u z z M o c k = \ M o c k e r y : : m o c k ( ' \ B u z z \ B r o w s e r ' ) ; $ b u z z M o c k - > s h o u l d R e c e i v e ( ' g e t ' ) - > a n d T h r o w ( ' \ B u z z \ E x c e p t i o n \ C l i e n t E x c e p t i o n ' ) ; $ l o g g e r M o c k = \ M o c k e r y : : m o c k ( ' \ M o n o l o g \ L o g g e r ' ) ; $ l o g g e r M o c k - > s h o u l d R e c e i v e ( ' i n f o ' ) - > n e v e r ( ) ; $ l o g g e r M o c k - > s h o u l d R e c e i v e ( ' e r r ' ) - > o n c e ( ) ; ( n e w \ A p i C o n s u m e r ( $ b u z z M o c k , $ l o g g e r M o c k ) ) - > g e t C u r r e n t M o o d ( ) ; } }

Slide 33

Slide 33 text

We providing API for external consumers it can't be tested...

Slide 34

Slide 34 text

Use Symfony's WebTestCase to test your API I call it integration tests

Slide 35

Slide 35 text

Call your API and check if it returns prepared data c l a s s E x p e n d i t u r e C o n t r o l l e r T e s t e x t e n d s W e b T e s t C a s e { u s e I s o l a t e d T e s t s T r a i t ; / / i t r e s e t s t e s t e n v i r o n m e n t p u b l i c f u n c t i o n t e s t G e t L i s t I n J s o n ( ) { $ c l i e n t = s t a t i c : : c r e a t e C l i e n t ( ) ; $ c l i e n t - > r e q u e s t ( ' G E T ' , ' / e x p e n d i t u r e s . j s o n ' ) ; $ j s o n = j s o n _ d e c o d e ( $ c l i e n t - > g e t R e s p o n s e ( ) - > g e t C o n t e n t ( ) ) ; $ t h i s - > a s s e r t T r u e ( $ c l i e n t - > g e t R e s p o n s e ( ) - > i s S u c c e s s f u l ( ) ) ; $ t h i s - > a s s e r t C o u n t ( 8 0 , $ j s o n ) ; $ t h i s - > a s s e r t G r e a t e r T h a n O r E q u a l ( n e w \ D a t e T i m e ( $ j s o n [ 7 9 ] - > c r e a t e d _ a t ) , n e w \ D a t e T i m e ( $ j s o n [ 0 ] - > c r e a t e d _ a t ) ) ; } }

Slide 36

Slide 36 text

Use fixtures and reset environment IsolatedTestsTrait should do the trick

Slide 37

Slide 37 text

Steps required to effectively run in isolation 1. configure PDO SQLite in file 2. create database 3. drop schema 4. load fixtres 5. copy database as a backup 6. copy database from backup for every test 7. delete database backup after test suite

Slide 38

Slide 38 text

Requirements changes frequently we can't keep up with unit tests

Slide 39

Slide 39 text

Use behavioral approach Behat in PHP

Slide 40

Slide 40 text

Describe features as scenarios It will be readable for: business, developers and machines

Slide 41

Slide 41 text

Perfectly fits into Agile process

Slide 42

Slide 42 text

Simple example F e a t u r e : l o o k f o r a j o b I n o r d e r t o f i n d c o o l j o b A s a n a s p i r i n g p r o g r a m m e r I n e e d t o b e a b l e t o l i s t j o b o f f e r s S c e n a r i o : l i s t o f f e r s f o r P L v e r s i o n G i v e n I a m o n " / " T h e n I s h o u l d s e e " D o ł ą c z d o t e a m u " A n d c l i c k " D o ł ą c z d o t e a m u " T h e n I s h o u l d b e o n " / k a r i e r a " A n d I s h o u l d s e e " P H P S e n i o r D e v e l o p e r ( G l i w i c e ) " S c e n a r i o : n o o f f e r s f o r E N s i t e G i v e n I a m o n " / e n " T h e n I s h o u l d n o t s e e " D o ł ą c z d o t e a m u " A n d I s h o u l d n o t s e e a n " # j o i n - u s " e l e m e n t

Slide 43

Slide 43 text

Our tests are slow!

Slide 44

Slide 44 text

This could be true ...

Slide 45

Slide 45 text

Always use in-memory sqlite database Or create clean sqlite database and copy it for every test

Slide 46

Slide 46 text

Group your tests Use PHPUnit's @group or Behat's @tag

Slide 47

Slide 47 text

Create "smoke tests" groups Those should be fast test, which ensures your system is most likely stable

Slide 48

Slide 48 text

Slower tests should run during night

Slide 49

Slide 49 text

Facts

Slide 50

Slide 50 text

It gives you confidence about changes and your code

Slide 51

Slide 51 text

Team is able to rapidly experiment with code

Slide 52

Slide 52 text

TDD enforces better Object Oriented design Smaller units of code and lower coupling always leads to better understanding of the codebase by future devs

Slide 53

Slide 53 text

End user experiences better quality lower 500 error ratio in the production

Slide 54

Slide 54 text

Happier users ↓ more $$$ in future

Slide 55

Slide 55 text

Conclusion

Slide 56

Slide 56 text

Setting up an working environment for automated tests is timely costly at the begining, but it pays off in the future

Slide 57

Slide 57 text

System without any kind of automated tests has big potential to be a big ball of mud

Slide 58

Slide 58 text

Good coverage can be easily achieved with mix of unit, functional and behavioral tests

Slide 59

Slide 59 text

You need to build and cultivate TDD culture in your surrounding

Slide 60

Slide 60 text

Thank you so much for attending! Feedback is much appreciated [email protected] Twitter: @sznapka GitHub: @wowo

Slide 61

Slide 61 text

Join team :-) XSolve