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

Choose Your Own Testing Adventure

Choose Your Own Testing Adventure

Going through the software testing process is sort of like reading through a Choose Your Own Adventure® book. Our goal is to make testing choices that will lead to a happy ending.

Chooseco now owns the trademark to Choose Your Own Adventure®. You can find the online at https://www.cyoa.com/.

Michael Stalker

February 10, 2015
Tweet

More Decks by Michael Stalker

Other Decks in Programming

Transcript

  1. – The Story of a Test Suite In which you,

    the Adventurer, And your band of Companions Set out to explore The Land of Testing.
  2. – ™ Should our tests interact with the database? ™ Should we

    use mocks? ™ Should we reuse test setup? ™ Should our tests call a remote service? ™ Should we give up if methods are hard to test? ™ Should we test error message text? Things Turn Confusing
  3. – ™ The test suite is slow ™ Tests break easily ™ Tests

    require too much setup ™ Tests are hard to write ™ Tests fail intermittently ™ Tests stay broken Dragon Smells
  4. – ™ To test because it's a Good Idea™, turn to

    slide 132. ™ To test because: – You want to verify basic correctness – You want to refactor more easily – You want to identify problems in your design – You want to catch regressions in the future – ...Turn to slide 12. Choices
  5. – ™ To test because it's a Good Idea™, turn to

    slide 132. ™ To test because: – You want to verify basic correctness – You want to refactor more easily – You want to identify problems in your design – You want to catch regressions in the future – ...Turn to slide 12. Choices
  6. class  User      attr_reader  :name        

     def  initialize(name:  'Unknown')          @name  =  name      end   end       class  Image      attr_reader  :id,  :user          def  initialize(user:  User.new)          @user  =  user          @id  =  #  generate  unique  id      end   end
  7. describe  Image  do      let(:user)  {  User.new  }  

       let(:image)  {  Image.new  }          describe  '#initialize'  do          it  'generates  an  ID'  do              expect(image.id).to  be          end              it  'sets  the  user'  do              image  =  Image.new(user:  user)              expect(image.user).to  eql(user)          end      end   end
  8. describe  Image  do      let(:user)  {  User.new  }  

       let(:image)  {  Image.new  }          describe  '#initialize'  do          it  'generates  an  ID'  do              expect(image.id).to  be          end              it  'sets  the  user'  do              image  =  Image.new(user:  user)              expect(image.user).to  eql(user)          end      end   end
  9. describe  Image  do      let(:user)  {  User.new  }  

       let(:image)  {  Image.new  }          describe  '#initialize'  do          it  'generates  an  ID'  do              expect(image.id).to  be          end              it  'sets  the  user'  do              image  =  Image.new(user:  user)              expect(image.user).to  eql(user)          end      end   end
  10. – Choices ™ To write your tests like this, turn to

    slide 51. ™ To use a test setup only when it applies to all tests in your block, turn to slide 78.
  11. – Choices ™ To write your tests like this, turn to

    slide 51. ™ To use a test setup only when it applies to all tests in your block, turn to slide 78.
  12. describe  Image  do      describe  '#initialize'  do    

         it  'generates  an  ID'  do              image  =  Image.new              expect(image.id).to  be          end              it  'sets  the  user'  do              user  =  User.new              image  =  Image.new(user:  user)              expect(image.user).to  eql(user)          end      end   end
  13. describe  Image  do      let(:user)  {  User.new(name:  'Moe')  }

         let(:image)  {  Image.new(user:  user)  }          #  100  lines  of  code          describe  '#user_name'  do          it  'says  who  uploaded  image'  do              user_name  =  image.user_name              expect(user_name).to  eql('Moe')          end      end   end
  14. – Choices ™ To write your tests like this, turn to

    slide 18. ™ To duplicate some test setup in several tests, turn to slide 31.
  15. – Choices ™ To write your tests like this, turn to

    slide 18. ™ To duplicate some test setup in several tests, turn to slide 31.
  16. describe  Image  do          #  100  lines

     of  code          describe  '#user_name'  do          it  'says  who  uploaded  image'  do              user  =  User.new(name:  'Moe')              image  =  Image.new(user:  user)              user_name  =  image.user_name              expect(user_name).to  eql('Moe')          end      end   end
  17. module  Postable      def  post        

     #  ...      end   end       class  BlogPost      include  Postable   end       class  Image      include  Postable   end
  18. describe  BlogPost  do      describe  '#post'  do    

         #  ...      end   end       describe  Image  do      describe  '#post'  do          #  ...      end   end
  19. – ™ To test your modules through classes that include them,

    turn to slide 117. ™ To create a dummy class to help test your modules, turn to slide 95. Choices
  20. – ™ To test your modules through classes that include them,

    turn to slide 117. ™ To create a dummy class to help test your modules, turn to slide 95. Choices
  21. class  TestPostable      include  Postable   end    

      describe  Postable  do      describe  '#post'  do          it  'creates  a  post'  do              postable  =  TestPostable.new              expect(postable.post).to  #...          end      end   end
  22. module  ImageHelper      def  name        

     "Image  #{params[:image_id]}"      end   end  
  23. – ™ To use RSpec Rails helper specs, turn to slide

    43. ™ To test helpers like any other module, turn to slide 64. Choices
  24. – ™ To use RSpec Rails helper specs, turn to slide

    43. ™ To test helpers like any other module, turn to slide 64. Choices
  25. module  ImageHelper      def  name        

     "Image  #{params[:image_id]}"      end   end  
  26. module  ImageHelper      def  name(id:  params[:image_id])      

       "Image  #{params[:image_id]}"      end   end  
  27. class  TestImageHelper      include  ImageHelper   end    

      describe  ImageHelper  do      describe  '#name'  do          it  'returns  an  image  name'  do              helper  =  TestImageHelper.new              name  =  helper.name(id:  4)              expect(name).to  eql('Image  4')          end      end   end  
  28. module  ImageHelper      def  name(id:)        

     "Image  #{id}"      end   end  
  29. class  User  <  ActiveRecord::Base      has_many  :friends,  through:  :friendships

         has_many  :friendships          def  befriend(friend)          friendships.create(friend:  friend)      end   end       class  Friendship  <  ActiveRecord::Base      belongs_to  :user      belongs_to  :friend,  class_name:  'User'   end  
  30. describe  User  do      describe  '#befriend'  do    

         it  'creates  a  friendship'  do              user  =  User.create              friend  =  User.create              user.befriend  friend              expect(user.friends.count).to  eql(1)              expect(Friendship.count).to  eql(1)          end      end   end  
  31. – Choices ™ To write tests that talk to the database,

    turn to slide 49. ™ To write tests that use mocks and stubs, turn to slide 104.
  32. class  User      def  self.password        

     SecureRandom.hex      end   end       User.password   #=>  "d929992ff842f257c89d8ad3cbb80f1b"       allow(User).to  receive(:password).                                and_return('hi')       User.password   #=>  "hi"  
  33. class  User      def  self.password        

     SecureRandom.hex      end   end       User.password   #=>  "d929992ff842f257c89d8ad3cbb80f1b"       allow(User).to  receive(:password).                                and_return('hi')       User.password   #=>  "hi"  
  34. class  User      def  self.password        

     SecureRandom.hex      end   end       User.password   #=>  "d929992ff842f257c89d8ad3cbb80f1b"       expect(User).to  receive(:password).                                  and_return('hi')       User.password   #=>  "hi"  
  35. – ™ With a stub, we allow User to receive the

    password message. ™ With a mock, we expect User to receive the password message. It Looks the Same to Me
  36. – ™ Mock – When you call a method on another object...

    – Or you want to talk to an external service... – And that operation has side effects. ™ Stub in all other cases. When Do You Mock?
  37. – Choices ™ To write tests that talk to the database,

    turn to slide 49. ™ To write tests that use mocks and stubs, turn to slide 104.
  38. – Choices ™ To write tests that talk to the database,

    turn to slide 49. ™ To write tests that use mocks and stubs, turn to slide 104.
  39. describe  User  do      describe  '#befriend'  do    

         it  'creates  a  friendship'  do              user  =  User.new              friend  =  User.new              friendships  =  Object.new              allow(user).to  receive(:friendships).                                            and_return(friendships)              expect(friendships).                  to  receive(:create).                  with(friend:  friend)              user.befriend  friend          end      end   end  
  40. describe  User  do      describe  '#befriend'  do    

         it  'creates  a  friendship'  do              user  =  User.new              friend  =  User.new              friendships  =  Object.new              allow(user).to  receive(:friendships).                                            and_return(friendships)              expect(friendships).                  to  receive(:create).                  with(friend:  friend)              user.befriend  friend          end      end   end  
  41. describe  User  do      describe  '#befriend'  do    

         it  'creates  a  friendship'  do              user  =  User.new              friend  =  User.new              friendships  =  Object.new              allow(user).to  receive(:friendships).                                            and_return(friendships)              expect(friendships).                  to  receive(:create).                  with(friend:  friend)              user.befriend  friend          end      end   end  
  42. describe  User  do      describe  '#befriend'  do    

         it  'creates  a  friendship'  do              user  =  User.new              friend  =  User.new              friendships  =  Object.new              allow(user).to  receive(:friendships).                                            and_return(friendships)              expect(friendships).                  to  receive(:create).                  with(friend:  friend)              user.befriend  friend          end      end   end  
  43. class  User  <  ActiveRecord::Base      has_many  :friendships    

     has_many  :friends,  through:  :friendships        def  befriend(friend)          friendships.create(friend:  friend)      end   end     class  Friendship  <  ActiveRecord::Base      belongs_to  :user      belongs_to  :friend,  class_name:  'User'   end  
  44. describe  User  do      describe  '#befriend'  do    

         it  'creates  a  friendship'  do              user  =  User.new              friend  =  User.new              friendships  =  Object.new              allow(user).to  receive(:friendships).                                            and_return(friendships)              expect(friendships).                  to  receive(:create).                  with(friend:  friend)              user.befriend  friend          end      end   end  
  45. describe  User  do      describe  '#befriend'  do    

         it  'creates  a  friendship'  do              user  =  User.new              friend  =  User.new              friendships  =  Object.new              allow(user).to  receive(:friendships).                                            and_return(friendships)              expect(friendships).                  to  receive(:create).                  with(friend:  friend)              user.befriend  friend          end      end   end  
  46. #  spec_helper.rb     RSpec.configure  do  |config|      config.mock_with

     :rspec  do  |mocks|          #  Prevents  you  from  mocking  or  stubbing          #  a  method  that  does  not  exist  on  a          #  real  object.  This  is  generally          #  recommended.          mocks.verify_partial_doubles  =  false      end   end  
  47. class  User      def  self.password        

     SecureRandom.hex      end   end  
  48. describe  User  do      describe  '.password'  do    

         it  'sets  a  password'  do              allow(SecureRandom).                  to  receive(:hex).                  and_return('hi')              expect(User.password).to  eql('hi')          end      end   end
  49. describe  User  do      describe  '.password'  do    

         it  'sets  a  password'  do              generator  =  OpenStruct.new(hex:  'hi')              password  =  User.password(generator)              expect(password).to  eql('hi')          end      end   end  
  50. class  User      def  cancel_account        

     friendships.each  {  |f|  f.destroy  }              #  35  lines  of  code...              data  =  RemoteAuth.fetch(self)          @success  =  !!data.auth.credentials          @success  =  true  if  self.admin?              #  43  lines  of  nested  conditionals...                    @success  &&  self.disable!      end   end  
  51. – 1.  Do whatever you need to do to get

    the code under test. 2.  Improve the code. 3.  Improve your tests. Testing Awful Code
  52. – ™ Test these with high-level acceptance tests. ™ These are not

    unit tests. ™ Test whatever your customers require. Testing Views
  53. – ™ “Mocks Aren’t Stubs” by Martin Fowler ™ Practical Object-Oriented Design

    in Ruby by Sandi Metz, Chapter 9 ™ Better Specs website: http://betterspecs.org/ ™ “Eradicating Non-Determinism in Tests” by Martin Fowler ™ Working Effectively with Legacy Code by Michael Feathers Resources
  54. – ™ "Magic Tricks of Testing" video by Sandi Metz ™ "The

    Deep Synergy Between Testability and Good Design" video by Michael Feathers ™ "Integrated Tests Are a Scam" by J. B. Rainsberger ™ Growing Object-Oriented Software, Guided by Tests by Steve Freeman and Nat Pryce Resources
  55. – ™  Princess: https://www.flickr.com/photos/partymonstrrrr/7316827188 ™  Stairs: https://www.flickr.com/photos/gadl/288607676/ ™  Fork: https://www.flickr.com/photos/james_wheeler/9468151425/in/photolist-fqEKy6-aoQZSe-aoTDkf-aoQpTe-bDvjFh-aoQSwX-obecdX-

    osGQuP-ousNGp-9YWC78-aLa7Xr-aL9ZZD-a6M8yk-azNXX8-cQH5i-8aFCyi-5Tm8ki-5GfYv1- NaWSn-9u1p76-9u5a9m-9u1hsB-7Xte9q-9wGrzK-9wJTQS-bgS4wt-fxqosn-7Xtfq1-oFSs4n-7Xtd8f-9u1yzB-fxqovF-aqxDuM-6ecY8M-7XqcBc- pYLqLL-gwPZT5-9wHi8E-7XtbVS-9wG2Ex-8wZJoN-6y4YNX-9wGFyZ-oppLGG-cX9kBG-8nXMfx-dj9TqW-4UdHYf-fSZfKw-hhSXSJ ™  Scaffolding: https://www.flickr.com/photos/ldm/14296081/in/photolist-2ggJa-4myKSw-puD54a-oTLyBM-CxLG9-5Ydz2c-5mCAiL-61wjbq- p3Cnx-gCPgU-GbAqX-mTCufc-AtiPD-6c917Q-8o7qEJ-2vGSqn-pei74m-zBk6f-2BWUfA-6BSGwe-chbj37-95FUn3-n8CpB1- oR6vrB-6BTnfr-7RGNc-r3Bay-7Bjx3X-2Ax8i-81yaU-fFz5Km-5c7bcQ-58ftpq-kMTAA3-o7rFHf-kTC7D-2Qu83N-5qGhKP-6xmHDy-2QpsTZ- e1EL6K-6uThwX-4WhnYr-jgjbyi-6o3d5M-6dgXV-7CCdmo-9T53yA-Mcs7-59nqz6 ™  Fragile: https://www.flickr.com/photos/motleye/299066723/in/photolist-sqN7P-2FKJdW-7tGnYi-4a113L-ZAmwd-56Nv5x- qvbVmR-92Kyy6-vfKPe-9JMT85-6p47kz-4vV9Lu-4EB1Mk-bKWJQZ-9UDVEj-7nVY7d- dPZ4Ai-8cgiwS-8rf7dd-4zKAvi-7FdymG-6JtUc5-7Pvwtk-8UqU1D-5evTWi-JZnqm-YACqD-7LgMJ-5cG3mE-u6FCP-LVbcE-bcWjf-PVzDe- agQsjv-2bXT1-cmaiQY-dWhD47-8u3LkG-8brFfM-5pmNuf-9XoYim-8DFyYi-4YRtwa-8DRaan-4v8tXj-77SaJB-fbdQLT-daiJH9-x5SEZ-85evxp ™  Shattered glass: https://www.flickr.com/photos/llreadll/3573278617/in/photolist-6rKZf2-9wMW41-auepW-fcq6Sn-9y7MJY-6wYJ93- hmpUsV-bdarj2-ipFk7P-hneMhN-7mWPp6-4Xikka-4yMuKd-9oe7DW-9kCF4D-55aqGQ-nWpoWs-nUsvEW-nYhnKF-nWcR4D-nE1o4c- nUsrRo-6yQYdG-fFJRgk-nWo5yf-nE1mVv-nE1wZ7-nYhit4-nE1vG7-nE1Eqw-4RstYi-jU4GZr-6yLTDB-6jj6iP-9yzyS9-5sCpkg-5gqPZS-8hau8B- dixF9v-nWcMdH-6EMigZ-5C3Kjr-n8jb7G-nE1DtS-eirzdV-6YY6st-5o5fBq-eAYX3-482GPi-nYhkrc ™  Roulette: https://www.flickr.com/photos/dahlstroms/5276348473/in/photolist-93fEzp-JfaHg-35MUP4-85EG4- oeJMvw-552jcY-85siop-7Zoewm-jwMLM5-85ECJ-bvk3Z2-jwP3qu-jwMNvq-bd5iG8-6HBj6o-q15ds-3LgDuT-bvk2P4-24fYe-kkux8c- fPvDam-8Edcnh-2inCkM-A2zMe-ettS6G-8YsJjo-85ESK-ecDJBz-9ZEmF5-7LYYdC-5WejBA-4K84MW-4k6Z9S-h1Egb-62LiHv-c7Es5- bqQP71-7LYZaf-7LYZ1h-7LYYQ7-7LYYvy-7LUZRi-7LYXKW-7LUZoe-7LUZep-7LYXkG-ds1dL-3htGiH-54t6Xs-5dkLUi ™  Head in sand: https://www.flickr.com/photos/12023825@N04/2898021822/in/photolist-5q67Vu-5eZ35e-6s3LMv-5kxw4o- CF6Mf-3dbCaX-3bcR41-6nDw8D-4HPZnj-4k6enD-A2ztT-3pGpRp-55zo6b-eVYp5-6aLURj-4gVAQh-6TWNsZ-68bTZR-e715B9-e6Ur3K- e714M7-e6UpYR-e713vA-e712Fq-kkuyJi-gok3nj-gokD9n-bTEW2K-itoqPn-9WRvGS-4Fog1W-5u6BTE-9pUy37- jtEN2c-4bLptM-2Vx99E-37XGWF-5J19Y9-bxEedx-4Fom7j-4Fojw1-4Fj4mM-4Foe9w-9BKXhj-4JvTxa-jwP1Jo-gokfRC-rhshf-7yd892-kPgMiW Photo Credits
  56. – ™  Dead end: https://www.flickr.com/photos/admaust/3652271486/in/photolist-6yJR53-7Cjksz-jyVaQ-ccyFx7-697Lo1-hUWDN-dPF5jL- pn3T3H-hUWDJ-9hXhsj-ehCiXi-uWZVP-54SD3T-fgFNwW-fgrzWi-666aDt-33sdMG-dGLkML-fgrwHZ-h6wSi1-3ar4sx-cewYs5- j1XQNa-8x8Mhx-dA6BQT-9vWSeV-fgryUB-fgrPY8-dPzpDt-fgFUco-fgFRTS-fgrCxa-fgrE7t-fgFWGG-fgrPbF-fgG46Y-fgFXGQ-fgrJ4B-dPzf2g- q7zHCB-eimrew-7DRMDe-6xahq3-4LAEzX-fxiMfr-eAui1P-81g9Eo-81gdnj-daiC92-btPK3T ™  Colored

    ropes: https://www.flickr.com/photos/derekbruff/7923579630 ™  Switchboard: https://www.flickr.com/photos/john_oshea/4484944821/in/ photolist-7QjvQR-7PFGoB-7PFFx4-7PJSEL-7PFDJ4-7PJQS9-7PFCeV-7PJPCj-7PFAXc-7PJNgQ-7PFAaV-7PFzWV-7PJLGJ-7PFyYR-7PJKRL-7PJJB 5-7PFwmD-7PFw1e-7PFvJg-7PFvpp-7PJG6C-7PFuje-7PFu4D-7PgcPJ-7Pcdza-7PcdwK-7Pcd8p-7LLJYp-7DUpsf-7Cg9s4-7PJFho-7PJENL-7Pi2fN -7Pe38n-7Pi229-7Pi1WC-7Pi1Ew-7PhUss-7PhUaw-7PgdsC-7Pgdpb-7PceaX-7Pgdkj-7Pce7k-7Pce5g-7Pce3V-7Pgddf-7PcdYB-7Pgd9N-7PcdUn ™  Dartboard: https://www.flickr.com/photos/modenadude/3280286776/in/photolist-5ZSjYy-7mwULw-98ZaVX-qgxQrk-kwr3wg- bXc6Nr-3t5Rre-9VWTG2-bpe8nT-bVHkvu-5AbmJU-cuqzWS-RDVWQ-bLkxC-5bje6L-mDfvU-4GvSvB-kwt9po-87YVmc-uo1r-5smnjs-CL6At- emAYd3-4s6DT-4s6CU-3wbWHS-RrWDs-cWz9Rf-8g4pLV-84HeXZ-aLiXcc-88Jcnx-4DCYsF-8NuypS-26erMn-5s3Vt1-6anxmk- msZRJ-3dZC5V-3a4uBb-4Qvpao-66mZ1h-87fJpK-87iVUS-aQL7Lg-4s6Ce-4rx9gB-3rtJJj-6UPf3p-A4VYA ™  Help key: https://www.flickr.com/photos/llauren/42440652/in/photolist-4Kw8o-7Vo5Zj-3nReW8-vsmJE-oCNzQL-bf2ecv- eiwhM1-7Lvaqu-7ntiiQ-V4RPF-avtc7U-7MaVuG-9sk6t4-bjxsyY-2R97kJ-6ZHmae-bf2eaF-6sUvAX-t8Rkn-aS4KWz-K5bqW-bf2e8Z- bf2e2g-7YtWQD-oYSo5a-5kRjMn-qcQWQd-cVnNMU-bf2e7P-kibQFh-7YDJb3-6ZHmmX-8pMZ2W-4ZagAF-5hZwdZ- bVpXwD-7w9QMb-6Jnxr1-baFaCV-6agmPN-gW12Do-h3vQbW-8wyXRz-inW8ZS-aKE6G-3jWBHb-6D7X7N-4mQaB9-98fCGk-9qG29R ™  Model airplane: https://www.flickr.com/photos/38978098@N03/8479068025/in/photolist-dVgrw2-dVgrvz-mAqqom-bt5sHq- bkox1b-9vdJFi-8ueRuL-pMTVcB-eb1VmY-dwbTmR-pLRy8g-bQnqfM-o3phyg-dHMWDH-oKGjGR-9g4TxP-6338pC-dsHD6h-57BLTE- mKqmQp-5Y4k6t-4Zs7Rn-cEFu25-9vgJtC-e1UZwk-nF2BNo-ndxYoi-p6Vy6q-byeiHp-p5qELV-ahtt74-5PfDES-aBSob4-cEFtP7-dJdUpJ-otcK2m- oQ17fA-8TE8k4-8TE8f2-9rzQU7-8TE8xg-9Fa6tp-24hqBN-dJ8qsx-24pzDM-25aurM-9NdQmT-23qvFi-dJdUM5-24caaZ ™  Ring of fire: https://www.flickr.com/photos/courosa/3786392249 ™  Elvis: "ElvisPresleyAlohafromHawaii" by Source. Licensed under Fair use via Wikipedia - http://en.wikipedia.org/wiki/ File:ElvisPresleyAlohafromHawaii.jpg#mediaviewer/File:ElvisPresleyAlohafromHawaii.jpg Photo Credits
  57. – ™  Elvis impersonator: https://www.flickr.com/photos/22197407@N04/3336836212/in/photolist-65Sa9Q-cwC7N-fwGZyg-sqkr- sqjW-8y4ydT-8y8rpG-cD6tZN-3oSne-cCXpLf-aeUsuq-8ZLiB-6G3wAo-cCXtXA-NLb56-cCXrVG- cD6vjd-6FYtmp-6FYs6c-6esPhp-cMJabq-cD6uAu-6FYt5F-cCXwCQ-cCXkkm-cMJkm3-cMJn4b-6FYutT-cMJkSo-cCXvy9- pwUqv2-cCXm45-cD6vzG-3rfLC-x8SWo-x2ZWG-6oksf-8psPuj-cCXqBN-cCXsT7-afsH3S-cMJ69q-37dnxd-g6kht9- cD6AvN-4Vc22L-cD6A1C-cD6ujQ-cCXnfE-9HxX9m ™ 

    Johnny Depp and Tony Angelotti: https://lh6.googleusercontent.com/-CQzo9ZttYMc/UfhSwVdYj0I/AAAAAAAAAPY/ PuX_XNn4RbE/w1101-h824-no/TonyAngelotti2.jpg. Used with permission. ™  Happily ever after: https://www.flickr.com/photos/stampinmom/5373581438 ™  Puzzle: https://www.flickr.com/photos/filu/4899342532/in/photolist-8sWpQY-iSEFkV-nLtYsi-9cfBr2- dKfwc5-5VxirZ-7UyLLa-9N7DnM-59ktec-59pCrN-6y2jMX-q6b1BC-6mSB7s-5UhnaA-6dpV1C-h82ub-4fUsNL- aSj57v-9N7Dc8-5Ycmai-8BPU1e-gJ3my-e3KE4-5B3jzN-G1RWd-5g6PTY-7vEVv7-6bZkwr-7vEVHQ-9boLH5-55TSMz- bpMqEM-8NLnCe-2QP4c-9bdfAr-4j1h7w-cee2wo-3KDNbn-p8FnFs-efQtxi-9SiXWP-7ASTkd-4jpvQH-7vB7fR- oGrQrW-57gbdz-4qB8yx-59pGDh-bqQCDE-oNLrbw ™  Dragon: http://pixabay.com/en/dragon-lizard-monster-chinese-149393/ Photo Credits